對於這個問題,首先按照《程式設計之美》中的分析對這個問題進行一定的簡化。從2n個數中找n個元素,有三種可能:大於sum/2,小於sum/2以及等於sum/2。而大於sum/2與小於等於sum/2沒區別,故可以只考慮小於等於sum/2的情況。
動態規劃第一步,分析子問題:
這裡我們用乙個三維陣列f表示子問題,f[i][j][k]表示前i個元素中選取j個元素,使得其和不超過k且最接近k。這個子問題可以根據第i個元素是否選擇來進行分析:
如果我們想回溯找到一組合理的分割方式,那麼在子問題的求解過程中,就要記錄有效的路徑,這樣我們再用乙個三維陣列path來記錄。
偽**如下所示:
1 f = 0view code2 path = 134
for i = 1 to 2*n
5 nlimit =min(i, n)
6for j = 1
to nlimit
7for k = 1 to sum/2
8 f[i][j][k] = f[i-1
][j][k]
9if k > a[i] && f[i-1][j-1][k-a[i]] + a[i] >f[i][j][k]
10 f[i][j][k] = f[i-1][j-1][k-a[i]] +a[i]
11 path[i][j][k] = 1
1213
return f[2n][n][sum/2], path
按照這樣思路,該演算法的時間複雜度為o(n^2*sum),空間複雜度也為o(n^2*sum)。
一下的分析,是針對上面的演算法,進一步優化。優化的方向為降低空間複雜度。
優化的思路借鑑了0-1揹包問題的思路,最外層迴圈是對元素的順序遍歷,這一步可以省略,為了保證每次計算時,問題f[i][j][k]的第i-1步為上次的狀態,那麼我們選擇倒敘遍歷變數j,這樣可以保證變數i-1狀態為上一步的狀態。
這樣改進之後的偽**是:
1 f = 0view code2 path = 034
for i = 1 to 2*n
5 nlimit =max(i, n)
6for j = nlimit to 1
7for k = 1 to sum/2
8if k > a[i] && f[j-1][k-a[i]] + a[i] >f[j][k]
9 f[j][k] = f[j-1][k-a[i]] +a[i]
10 path[i][j][k] = 1
1112
return f[n][sum/2], path
改進後,不儲存路徑的空間複雜度為o(n*sum),時間複雜度不變。
下面是根據記錄好的path回溯可行路徑的偽碼:
1 f = 0view code2 path = 034
for i = 1 to 2*n
5 nlimit =min(i, n)
6for j = nlimit to 1
7for k = 1 to sum/2
8if k > a[i] && f[j-1][k-a[i]] + a[i] >f[j][k]
9 f[j][k] = f[j-1][k-a[i]] +a[i]
10 path[i][j][k] = 1
1112
return f[n][sum/2], path
最後是我對演算法複雜度為o(n*sum)的c++實現:
1 #include 2view code3using
namespace
std;45
int min(int a, intb)6
1213
int split_array(int *array, int
nlength)
1442}43
}44}45
int result = f[nlength/2][sum/2
];46
47int i =nlength;
48int j = nlength/2;49
int k = sum/2;50
while(i > 0 && j > 0 && k >0)51
58 i--;59}
60 cout<
61return
result;62}
6364
intmain()65;
67 cout<10)<
68return0;
69 }
程式設計之美 陣列分割
問題1 有乙個無序 元素個數為n的正整數陣列,要求 如何能把這個陣列分割為兩個子陣列,子陣列的元素個數不限,並使兩個子陣列之和最接近。解答 int sum 0 for i 0 i0 j 從最大的開始遍歷,是為了防止同乙個數選取多次 for j sum 2 j if dp j break return...
程式設計之美 陣列分割
程式設計之美 陣列分割 和擴充套件 將乙個陣列劃分成兩個子陣列,要求他們的和最接近 1.長度要求相等的情況 2.長度沒有要求的情況 都能用動態規劃解決 include includeusing namespace std void arraysplit1 int a,int n 兩個子陣列長度要求相...
程式設計之美 陣列分割
問題 有乙個沒有排序,有2n個元素的陣列,要求把這個陣列分為兩部分,分別含有n個元素,並使兩個子陣列的和最接近。這裡的程式主要是計算這個和的值。比如陣列 計算後符合的分法是 和算出比較小的就可以了是 110 例如陣列 分開後是 和 思考方法 動態規劃 1 計算出陣列中所有元素的和,並把它除以2,這樣...