」的版主的介紹與分析:
《程式設計之美》2.18解法二中提到,從2n個數中找n個元素,有三種可能:大於sum/2,小於sum/2以及等於sum/2。而大於sum/2與小於等於sum/2沒區別,故可以只考慮小於等於sum/2的情況,在此我們仍然沿用這個思想。
起狀態轉移方程是:f[i][j][k]=max
其中,f[i-1][j][k]表示前i-1個元素中選取j個使其和不超過但最逼近k;f[i-1][j-1][k-a[i]]在前i-1個元素中選取j-1個元素使其和不超過但最逼近k-a[i],這樣再加上a[i]即第i個元素就變成了 選擇上第i個元素的情況下最逼近k的和。而第一種情況與第二種情況是完備且互斥的,所以需要將兩者最大的值作為f[i][j][k]的值。
偽**如下:
f← 0
for i ← 1 to 2*n
nlimit ← min(i,n)
do for j ← 1 to nlimit
do for k ← 1 to sum/2
f[i][j][k] ← f[i-1][j][k]
if (k >= a[i] && f[i][j][k] < f[i-1][j-1][k-a[i]]+a[i])
then f[i][j][k] ← f[i-1][j-1][k-a[i]]+a[i]
return f[2n][n][sum/2]
當然,前面已經提到,要給出一種方案的列印,下面我們談談怎麼列印一種方案。
可以設定乙個三維陣列path來記錄所選擇元素的軌跡。含路徑的偽**如下,只是在上述偽**中新增了一點**而已。
f← 0
path← 0
for i ← 1 to 2*n
nlimit ← min(i,n)
do for j ← 1 to nlimit
do for k ← 1 to sum/2
f[i][j][k] ← f[i-1][j][k]
if (k >= a[i] && f[i][j][k] < f[i-1][j-1][k-a[i]]+a[i])
then f[i][j][k] ← f[i-1][j-1][k-a[i]]+a[i]
path[i][j][k] ← 1
return f[2n][n][sum/2] and path
根據求得的path我們可以從f[2n][n][sum/2]往f[0][0][0]逆著推導來列印軌跡對應的元素。偽**如下:
i ← 2n
j ← n
k ← sum/2
while (i > 0 && j > 0 && k > 0)
do if(path[i][j][k] = 1)
then print a[i]
j ← j-1
k ← k-a[i]
i ← i-1
下面開始優化空間復制度為o(n*sum/2)
我們觀察前面不含路徑的偽**可以看出,f[i][j][k]只與 f[i-1]有關,這一點狀態方程上也能反映出來。所以我們可以用二維陣列來代替三維陣列來達到降低空間複雜度的目的。但是怎麼代替裡面存有玄 機,我們因為f[i][j][k]只與f[i-1]有關,所以我們用二維陣列來代替的時候應該對f[i][j][k]的「j」維進行逆序遍歷。為 什麼?因為只有這樣才能保證計算f[i][j][k]時利用的f[i-1][j]和f[i-1][j-1]是真正i-1這個狀態的值,如果正序遍 歷,那麼當計算f[j]時,f[j-1]已經變化,那麼計算的結果就是錯誤的。
偽**如下
f← 0
for i ← 1 to 2*n
nlimit ← min(i,n)
do for j ← nlimit to 1
do for k ← a[i] to sum/2
if (f[j][k] < f[j-1][k-a[i]]+a[i])
then f[j][k] ← f[j-1][k-a[i]]+a[i]
return f[n][sum/2] and path
上面的偽**基本上和《程式設計之美》2.18節最後所給的**基本一致了,但是裡面並不含path,如果要列印其中一種方案,那麼仍需要2n*n*sum/2的空間來存放軌跡。即
f← 0
path← 0
for i ← 1 to 2*n
nlimit ← min(i,n)
do for j ← nlimit to 1
do for k ← a[i] to sum/2
if (f[j][k] < f[j-1][k-a[i]]+a[i])
then f[j][k] ← f[j-1][k-a[i]]+a[i]
path[i][j][k] ← 1
return f[n][sum/2] and path
列印路徑的偽**與之前的一模一樣,這裡不再重寫。
#include #include #define sum_max 100
#define mlen 20
int min(int a,int b)
--i;
} printf("\n");
}int main()
; int len=sizeof(a)/sizeof(a[0]); //陣列長度
int sum=0;
int i;
for (i=0;i
C語言實現逆序輸出詳細
問題 c語言實現對陣列元素依次賦值0,1,2,3,4,5,6,7,8,9,然後按照逆序輸出。解題思路 顯然首先要定義乙個長度為10的陣列,由於賦給的值是整數,因此,陣列可以定義為整型,要賦值的是0 9,有一定規律,可以用迴圈來賦值。源 演示 include 標頭檔案 int main 主函式 pri...
c語言實現螺旋陣列
螺旋陣列 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9 分析 這是乙個一維維度和二維維度相等的陣列arr len len 將num 1開始從arr 0 0 賦值,直到num len len結束賦值。在賦值過程中,如...
C語言實現動態陣列
include include 要使用malloc是要包含此標頭檔案 include 要使用memset是要包含此標頭檔案 intmain for i 0 i 列印陣列 free p 釋放記憶體,malloc和free一定要記得成組使用,不然會導致程式吃記憶體 getchar 讓程式停頓,觀察輸出 ...