跟著程式設計之美學演算法 陣列分割

2022-05-08 21:09:08 字數 2418 閱讀 6801

對於這個問題,首先按照《程式設計之美》中的分析對這個問題進行一定的簡化。從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 = 0

2 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

view code

按照這樣思路,該演算法的時間複雜度為o(n^2*sum),空間複雜度也為o(n^2*sum)。

一下的分析,是針對上面的演算法,進一步優化。優化的方向為降低空間複雜度。

優化的思路借鑑了0-1揹包問題的思路,最外層迴圈是對元素的順序遍歷,這一步可以省略,為了保證每次計算時,問題f[i][j][k]的第i-1步為上次的狀態,那麼我們選擇倒敘遍歷變數j,這樣可以保證變數i-1狀態為上一步的狀態。

這樣改進之後的偽**是:

1 f = 0

2 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

view code

改進後,不儲存路徑的空間複雜度為o(n*sum),時間複雜度不變。

下面是根據記錄好的path回溯可行路徑的偽碼:

1 f = 0

2 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

view code

最後是我對演算法複雜度為o(n*sum)的c++實現:

1 #include 2

3using

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 }

view code

程式設計之美 陣列分割

問題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,這樣...