2.18 数组分割

题目概述:有一个没有排序,元素个数为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最接近的那个和,这便是答案。这个算法的时间复杂度是O(22N).

因为这个过程中只关注和不大于SUM/2的那个子数组的和。所以集合中重复的和以及大于SUM/2的和都是没有意义的。把这些没有意义的和剔除掉,剩下的有意义的和的个数最多就是SUM/2个。所以,我们不需要记录S(2N,N)中都有哪些和,只需要从SUM/2到1遍历一次,逐个询问这个值是不是在S(2N,N)中出现,第一个出现的值就是答案。我们的程序不需要按照上述递推公式计算每个集合,只需要为每个集合设一个标志数组,标记SUM/2到1这个区间中的哪些值可以被计算出来。

#include <stdio.h>
int min(int a,int b)
{
	return a>=b?b:a;
}
int halfsame(int array[],int n,int sum)
{
	int i,j,k;
	int flag[n/2+1][sum/2+1];
	for(i=0;i<=n/2;i++)
		for(j=0;j<=sum/2;j++)
			flag[i][j]=0;//选择i个数,和为j是否存在
	flag[0][0]=1;
	for(k=0;k<n;k++)//元素(0 ~ N-1)
		for(i=min(k+1,n/2);i>0;i--)//选择i个数(1~n/2)
			for(j=1;j<=sum/2;j++)//和为j(1~sum/2)
				if(j>=array[k]&&flag[i-1][j-array[k]])
					flag[i][j]=1;
	for(j=sum/2;j>0;j--)
	{
		if(flag[n/2][j]>0)
			return sum-2*j;
	}
}
int main()
{
	int s=0,num,i,result;
	int str[]={1,5,7,8,9,6,3,11,20,7};
	num=sizeof(str)/sizeof(int);
	for(i=0;i<num;i++)
		s+=str[i];
	result=halfsame(str,num,s);
	printf("difference between two half array:%d\n",result);
	return 1;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值