动态规划之数组分割问题

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

拿到题目后的解题思路就是得到2N个数和的一半,记为half,然后从2N个数中找到N个数,使得N个数相想加的和小于等于但是最接近half。

一开始计算时用了以下的代码:

		for(int i=1;i<=n2;i++){
        		for(int j=ave;j>=arr[i];j--){
        			dp[j] = getMax(dp[j],dp[j-arr[i]]+arr[i]);
        		}
        	}

但是仔细分析后发现,这个算法虽然能求出最接近half的值,但是却没有限定相加数的个数N,因此错误。


之后分析了正确的解法。

转解法地址:http://www.cppblog.com/baby-fly/archive/2009/09/24/92392.html

为了便于理解这个算法,先举一个小例子:有3、2、5三个数,求三个数中任意几个数相加小于8的各种结果。

1、首先,这是初始表,候选数是3、2、5,数组d[3][8],横向j代表数相加的和,纵向i代表相加数的个数。

动态规划之数组分割问题_第1张图片

2、开始处理候选数3,蓝色行代表正在处理的行,if(d[i-1][j-3]==Y)d[i][j]=d[i][j]=Y

动态规划之数组分割问题_第2张图片

3、开始处理候选数2,先处理两个数的情况,仍然依照上述公式,得到d[2][5]=Y

动态规划之数组分割问题_第3张图片

5、处理候选数为2,一个数的情况,得到d[1][2]=Y

动态规划之数组分割问题_第4张图片

6、处理候选数为5,三个数的情况,此时没有符合条件

动态规划之数组分割问题_第5张图片

7、处理候选数为5,两个数的情况,得到d[2][7]=Y 和 d[2][8] = Y

动态规划之数组分割问题_第6张图片

8、处理候选数为5,一个数的情况,得到d[1][5]=Y

动态规划之数组分割问题_第7张图片

9、处理结束。至此我们可以根据该图得出,3、2、5三个数中:(1)任意三个相加小于等于8的情况不存在;(2)任意两个数相加小于等于8的情况存在,最大值为8;(3)任意一个数相加小于等于8的情况存在,最大值为5.


三个结论可以转化成问题:在m个数中,任取n个数相加,使和小于等于但最接近数字k。

根据题目,这个模型就可以描述为:在2N个数中,任取N个数相加,使得和小于等于但是最接近half.

贴出原帖代码,上面示例就是对这个算法的一个弱化的演示

for(i = 0; i < N+1; i++)   
    for(j = 0; j < sum/2+1; j++)   
        flag[i][j] = false;   
flag[0][0] = true;   
for(int k = 1; k <= 2*N; 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]) {   
        cout << "minimum delta is " << abs(2*i - sum) << endl;   
        break;   
    }   
} 
至于为什么i是从大到小顺序,这是因为,如果从小到大循环,就会出现一个值重复计算的情况。

4、处理候选数为2,一个数的情况,得到d[1][2]=Y

你可能感兴趣的:(Algorithm记录)