数组分割问题主要是对一个给定的2*n的数组进行分割,分成两个大小为n的数组,使得两个数组之和尽量接近。
换种说法就是分成两个大小为n的数组之后,使得任意一个数组更接近数组和的一半即可,因此在算法中只需要考虑小于等于sum/2的情况即可。
此题采用动态规划来做,从2n个数中取i个数,在从i个数中取n个数(如果i不大于n,那么就是全部取),遍历这i个数中取n个数能够得到的所有的和的情况。
最后找出最接近数组一半的值。
注意在第二次循环中,j一定是从大往小遍历,而不能从小往大遍历,因为从小往大遍历会导致多计算一次a[i],比如从1到2这样遍历,那么如果当前a[i]=8,那么j=1时,会更新a[1][8]=true,j=2时,如果sum/2时大于16的,那么就会更新a[2][16]=true,这显然不对,相当于算了两次8。所以一定是从大往小遍历。
具体代码如下:
1 package test; 2 3 public class SplitTheArray{ 4 boolean [][] result; 5 public void splitArray(int [] a){ 6 int length = a.length; 7 int sum = 0; 8 int minus = 999; 9 for(int i=0;i<length;i++){ 10 sum+=a[i]; 11 } 12 result = new boolean[length/2+1][sum/2+1]; 13 result[0][0] = true; 14 for(int i=1;i<=length;i++){ 15 //从2n个数中选取n个数,当前新增加的数为a[i] 16 for(int j=min(i,length/2);j>=1;j--){ 17 //从i中选取k个数 18 for(int m=1;m<=sum/2;m++){ 19 //获取能由前i-1个数,得到m-a[i]的值的情况,将这些情况置为true 20 if(m>=a[i-1]&&result[j-1][m-a[i-1]]){ 21 result[j][m] = true; 22 } 23 } 24 } 25 } 26 for(int i=0;i<result[length/2].length;i++){ 27 if((sum/2-i)<minus&&result[length/2][i]){ 28 minus = sum/2-i; 29 } 30 } 31 System.out.println(sum/2-minus); 32 } 33 34 private int min(int a,int b){ 35 return (a>b)?b:a; 36 } 37 38 public static void main(String [] args){ 39 int a [] = {1,5,7,8,9,6,3,11,20,17}; 40 SplitTheArray sta = new SplitTheArray(); 41 sta.splitArray(a); 42 } 43 }