编程之美--2.18数组分割
有一个没有排序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近
解法一,暴力搜索,遍历每种分组方法,一共C(2N, N)种组合,复杂度是(2N!/N!),复杂度过高。
解法二,动态规划,原题是要求求两个数组的和最近接,这等价于要求其中较小的和最接近与2n个正整数的和(设为SUM)的一半。因此,弱化题目,求这个最近接一半的且小于等于SUM/2数值,定义Heap[i],i<=N表示任意i个数能够构成的数值集合。初始化:Heap[0]= 0。更新代码:
for(int i=1; i<2*N; i++) { // 依次读取A[i]更新堆 for(int j=min{i, N}; j>0; j--) // 更新引入A[i]后可能的元素个数的情况 for each v in Heap[j-1] // 对于引入A[i]的情况 insert(Heap[j], A[i]+v); }
解法3: 时间复杂度为O(N^2*SUM)
public static void main(String args[]){ int[] arr = {Integer.MIN_VALUE,1,10,5,6,7,3,4,2,8,0}; //总共10个元素(第一个不算) int sum = 0 ; for(int i = 1 ; i < arr.length ; i++) sum+=arr[i]; System.out.println("10个数的和为:"+sum); GetSum(arr,sum); } public static void GetSum(int[] arr, int sum){ //这里下标均从1开始 int len = arr.length-1; int n = len/2; //isOK[i][v]表示是否可以找到i个数,使得它们和为v boolean[][] isOK = new boolean[n+1][sum/2+1]; isOK[0][0] = true; //对每一个数字都进行遍历 for(int k = 1 ; k <=len ; k++){ //for(int i = 1 ; i <=k &&i<=n ; i++){ //要相加的数字的数量从Math.min(k,n)开始,减到1结束 //相当于从最后一行向上更新到第一行,从上向下会造成用到的数据被提前覆盖 for(int i = Math.min(k,n) ; i > 0 ; i--){ //对和的每一种情况都要进行判断 for(int v = 1 ; v <=sum/2 ; v++){ if(v>=arr[k]&&(isOK[i-1][v-arr[k]]==true)) isOK[i][v]=true; } } } for(int i = 1 ; i <= n ; i++){ for(int j = 1 ; j <= sum/2 ; j++){ System.out.print(isOK[i][j]+"\t"); } System.out.println(); } }
运行结果:
10个数的和为:46
true true true true true true true true false true false false false false false false false false false false false false false
true true true true true true true true true true true true true true true true true true false false false false false
false false true true true true true true true true true true true true true true true true true true true true true
false false false false false true true true true true true true true true true true true true true true true true true
false false false false false false false false false true true true true true true true true true true true true true true

浙公网安备 33010602011771号