码迷,mamicode.com
首页 > 编程语言 > 详细

编程之美:数组分割

时间:2016-07-31 13:07:34      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

题目概述:有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。

 

假设数组A[1..2N]所有元素的和是SUM。模仿动态规划解0-1背包问题的策略,令S(k, i)表示前k个元素中任意i个元素的和的集合。显然:
S(k, 1) = {A[i] | 1<= i <= k}
S(k, k) = {A[1]+A[2]+…+A[k]}
S(k, i) = S(k-1, i) U {A[k] + x | x属于S(k-1, i-1) }
按照这个递推公式来计算,最后找出集合S(2N, N)中与SUM最接近的那个和,这便是答案。
因为这个过程中只关注和不大于SUM/2的那个子数组的和。所以集合中重复的和以及大于SUM/2的和都是没有意义的。把这些没有意义的和剔除掉,剩下的有意义的和的个数最多就是SUM/2个。所以,我们不需要记录S(2N,N)中都有哪些和,只需要从SUM/2到1遍历一次逐个询问这个值是不是在S(2N,N)中出现,第一个出现的值就是答案。

例子:

比如说有一个6个数的数组,分成两个数组,其中每个数组有3个数:

[2,5,7,9,11,13]

计算可得:SUM=47,SUM/2=23.5

我们需要求的是:

k=6,i=3时,套入上面公式可得:

S(6,3)=S(5,3) U {13+{S(5,2)}}

集合S(5,3)={14,16,18,20,22,21,23,25,27}

集合S(5,2)={7,9,11,12,13,14,16,18,20}

13+S(5,2)={20,22,24,25,26,27,29,31,33}

最后:

S(6,3)={14,16,18,20,21,22,23,24,25,26,27,29,31,33}

我们在集合S(6,3)中,从SUM/2(23.5)到1遍历一次,逐个询问这个值是不是在S(6,3)中出现,第一个出现的值就是答案,也就是23,即答案。

 

public class Main {  
  
    public static void main(String[] args) {  
        int A[] = { 1, 2, 3, 5, 7, 8, 9 };  
        // int A[] = { 1, 5, 7, 8, 9, 6, 3, 11, 20, 17 };  
        func(A);  
    }  
  
    static void func(int A[]) {  
        int i;  
        int j;  
  
        int n2 = A.length;  
        int n = n2 / 2;  
        int sum = 0;  
        for (i = 0; i < A.length; i++) {  
            sum += A[i];  
        }  
  
        /** 
         * flag[i][j]:任意i个数组元素之和是j,则flag[i][j]为true 
         */  
        boolean flag[][] = new boolean[A.length + 1][sum / 2 + 1];  
        for (i = 0; i < A.length; i++)  
            for (j = 0; j < sum / 2 + 1; j++)  
                flag[i][j] = false;  
          
        flag[0][0] = true;  
          
        for (int k = 0; k < n2; k++) {  
            for (i = k > n ? n : k; i >= 1; i--) {  
                // 两层外循环是遍历集合S(k,i)  
                for (j = 0; j <= sum / 2; j++) {  
                    if (j >= A[k] && flag[i - 1][j - A[k]])  
                        flag[i][j] = true;  
                }  
            }  
        }  
        for (i = sum / 2; i >= 0; i--) {  
            if (flag[n][i]) {  
                System.out.println("sum is " + sum);  
                System.out.println("sum/2 is " + sum / 2);  
                System.out.println("i is " + i);  
                System.out.println("minimum delta is " + Math.abs(2 * i - sum));  
                break;  
            }  
        }  
    }  
}  

 

编程之美:数组分割

标签:

原文地址:http://www.cnblogs.com/Donnnnnn/p/5722750.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!