动态规划之数组分割问题

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

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

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

<span style="white-space:pre">		</span>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代表相加数的个数。


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


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


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



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



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


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


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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值