编程之美2.18——数组分割(动态规划问题)

五迷三道 提交于 2020-03-09 16:35:30

题目概述:有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。(啃了好久才啃明白,主要是动态规划可惜忘了,第一眼看不懂的童鞋洗洗睡睡明天继续研究哈,更多精彩请看《编程之美》)

第一想法:

是从2N个数的数组中提取所有N的组合情况,估计需要N个for循环,此时至少为N的阶乘的时间复杂度;然后想到动态规划的0-1背包(其实是看了原文才晓得的),将heap[M](M表示从2N中所有可能的M个元素和组成的集合),从下到上(m->1...->N)最终求的完整的heap[N]。要点:可以想成求不大于sum/2的最接近集合,以下为分析:

假设数组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/2最接近的那个和,这便是答案。

第二想法:
第一种算法时间复杂度随着N的增大而“狠大”,注意是很大,所以出现了第三种方法(书上说时间复杂度为n的平方乘以num),根据分析可以去求不大于sum/2的所有数能否用从2N中的N个提取的数组合出来,标记所有可能,把下面代码看懂就真懂了(看懂了就不难咯),代码如下:

#include <iostream>
#include <algorithm>

using namespace std;

#define MAXN 101
#define MAXSUM 100000
int A[MAXN];
bool dp[MAXN][MAXSUM];

// 题目可转换为从2n个数中选出n个数,其和尽量接近于给定值sum/2
int main()
{
	int n, i, k1, k2, s, u;
	cin >> n;
	for (i=1; i<=2*n; i++)
		cin >> A[i];
	int sum = 0;
	for (i=1; i<=2*n; i++)
		sum += A[i];
	memset(dp,0,sizeof(dp));
	dp[0][0]=true;
	// 对于dp[k][s]要进行u次决策,由于阶段k的选择受到决策的限制,
	// 这里决策选择不允许重复,但阶段可以重复,比较特别
	for (k1=1; k1<=2*n; k1++)//				// 外阶段k1
		for (k2=min(k1,n); k2>=1; k2--)		// 内阶段k2
			for (s=1; s<=sum/2; s++)	// 状态s
				// 有两个决策包含或不包含元素k1
				if (s>=A[k1] && dp[k2-1][s-A[k1]])//判断总和为s的数是否能够等于k2个提取的数之和
					dp[k2][s] = true;
	// 确定最接近的给定值sum/2的和
	for (s=sum/2; s>=1 && !dp[n][s]; s--);
	printf("the differece between two sub array is %d\n", sum-2*s);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!