編程之美2.18——數組分割(動態規劃問題)

題目概述:有一個沒有排序,元素個數爲2N的正整數數組。要求把它分割爲元素個數爲N的兩個數組,並使兩個子數組的和最接近。(啃了很久才啃明白,主要是動態規劃惋惜忘了,第一眼看不懂的童鞋洗洗睡睡明天繼續研究哈,更多精彩請看《編程之美》) ios

第一想法: 算法

是從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個提取的數組合出來,標記全部可能,把下面代碼看懂就真懂了(看懂了就不難咯),代碼以下: spa

#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);
}
相關文章
相關標籤/搜索