[编程之美] PSet2.18 数组分割

参考:http://blog.csdn.net/linyunzju/article/details/7729774

问题描述:

       有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。


分析与思路:

        从题目中可以看出,题目的本质就是从2n个整数中找出n个,使得它们的和尽可能靠近所有整数之和的一半。

解法一:

       这个问题存储的是从前k个数中选取任意个数,且其和为s的取法是否存在dp[k][s]。之所以将选出的数之和放在下标中,而不是作为dp[k]的值,是因为那种做法不满足动态规划的前提——最优化原理,假设我们找到最优解有k个数p1p2...pk(选出的这k个数之和是最接近sum/2的),但最优解的前k-1个数p1p2...pk-1之和可能并不是最接近sum/2的,也就是说可能在访问到pk之前有另一组数q1q2....qk-1其和相比p1p2...pk-1之和会更接近sum/2,即最优解的子问题并不是最优的,所以不满足最优化原理。因此我们需要将dp[k]的值作为下标存储起来,将这个最优问题转化为判定问题,用带动态规划的思想的递推法来解。

         代码如下:

//数组分割:(2n个数组中分成两半让各组之和最接近)
//使用dp[i][s]来标记能否找到i个数,使他们之和为s
//递推过程为dp[i][s]=dp[i-1][s]或dp[i-1][s-Arr[i]],复杂度O(N^2*Sum)
#define MIN(a,b) ((a)>(b)?(b):(a))
const int MAXLEN = 100;
const int MAXSUM = 1000;
int ArrPartition(int Arr[] , int arrLen )
{
	//---判断输入数组是否为偶数大小
	if(arrLen & 1)
		return -1;
	//---对数组求和
	int sum = 0;
	for(int i=0 ; i<arrLen ; i++)
		sum += Arr[i];
	//---定义判决数组dp
	bool dp[MAXLEN][MAXSUM];
	memset(dp,0,sizeof(dp));
	dp[0][0] = true;
	//---动态规划求解判决数组
	for(int i=1 ; i<=arrLen ; i++){//对于第i个数
		for(int j=MIN(i,arrLen/2) ;j>=1; j--)//i个数中取j个数,j的范围为[i,arrLen/2],由递推公式可知要自底向上求解
			for(int v=1 ; v<=sum/2 ; v++){
				if(v>=Arr[i-1] && dp[j-1][v-Arr[i-1]])//Arr下标从0开始,与dp下标从1开始不同。
					dp[j][v] = true;
			}
	}
	int s;
	for (s=sum/2; s>=1 ; s--)
		if(dp[arrLen/2][s])
			break;
	return sum/2-s;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值