題目概述:有一個沒有排序,元素個數爲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); }