数组分割问题主要是对一个给定的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 }