動態規劃揹包問題 01揹包(超詳細)

2021-10-01 09:37:08 字數 3467 閱讀 3200

問題背景:

設有容量為v的揹包,有n種物品,每件物品的體積是c[i],對應的價值是v[i],每種物品只有乙個,要使揹包中所裝物品的價值總和最大,問該如何選擇。

用乙個二維陣列f[i][j],來表示i件物品,在用j的容量時的最大的價值。因為對於每一件物品,你選擇就只能是裝或者不裝這件物品。

例如:物品: a b c d

體積: 5 4 6 10

價值; 10 7 14 25

當你揹包裝1件物品時,假如你的揹包容量為4,此時f[1][4]=7,你只能裝物品b,如果你揹包容量為9,這時你就有了選擇,能夠裝下物品a,b,c中的任何乙個,因為只能裝下一件物品,所以即使你能裝下a和b,你當然也只能選價值最大的物品c,即f[1][9]=14。

第一件物品當然是比較簡單,當揹包裝兩件物品時(i=2),當你揹包還能裝下第二件物品,此時你有兩個選擇,對第二件物品拿與不拿:

拿的話就是f[i][j] = f[i-1][j – c[i]] + v[i] 下劃線部分是拿第i個物品之前最多能拿的價值,再加上v[i](第i個物品的價值)這就是拿第i個物品時的最大價值。

不拿的話就是f[i][j] = f[i-1][j]就相當於裝i-1件物品的最大價值。當你揹包裝不下第二件物品的時候也是f[i][j] = f[i-1][j](裝不下了,還不是只能像前面一樣唄)

這時,我們每一步都是取的最佳方案(得到了最大價值)

我們就可以得出狀態轉移方程 f[i][j] = max(f[i-1][j], f[i-1][j – c[i]] + v[i])

目標就是f[n][v]

這裡有個難理解的地方就是初學者可能認為,f[i][j] = f[i-1][j – c[i]] + v[i]不是裝得下就裝了嗎?

並不是這樣!!! 當你的揹包容量v = 26時

我們不如打個表看看

比如f[3][25] = max(f[2][25], f[2][25-6] + 14) = max(39, 56)=56 這就不對了 為什麼呢?!

比如f[4][25] = max(f[3][25], f[3][25-10] + 25) = max(49,56)=56; 這又是對的

但是!!! 程式出來是這樣的

在這裡比如f[3][25] = max(f[2][25], f[2][25-6] + 14) = max(17, 31)=31

比如f[4][25] = max(f[3][25], f[3][25-10] + 25) = max(31,56)=56;

這才是正解(為什麼會這樣的原因,相信讀者能自己分析出來)

二維陣列的時間和空間複雜度都是o(vn)

有沒有發現,只有最後一行**才有用,這時我們想到了用一維陣列來優化。

時間複雜度不能再進行優化了,但可以將空間複雜度優化。

先寫出狀態轉移方程f[j] = max(f[j], f[j – c[i]] + v[i])

我們可以繼續分析一波資料

還是用上面的例子:

物品: a b c

體積: 5 4 6

價值; 10 7 14

當 i = 1 時

f[0] 到 f[4]顯然都是零

f[5] = max(f[5], f[0] + 10)= 10

f[6] = max(f[6], f[1] + 10) = 10

…f[9] = max(f[9], f[4] + 10) = 10

f[10] = max(f[10], f[5] + 10)=20

!!! 問題出現了,如果每件物品只拿一次,價值不可能出現20

仔細分析發現原來是物品a拿了兩次既f[10]不僅由f[5]決定了而且還被f[0]影響了,這顯然是不對的,這就是我們後面要講的完全揹包問題。

我們想出了乙個解決辦法,不是f[10]被前面多次重新整理嘛,我們第二層迴圈倒著來。j=14開始(我們不分析這麼多了揹包就取14)到j>=c[i]就可以了,因為前面都是0,沒有意義。

i = 1時

f[14] = max(f[14], f[9] + 10) = 10

f[13] = max(f[13], f[8] + 10) = 10

…f[5] = max(f[5], f[0] + 10) = 10

i = 2時

f[14] = max(f[14], f[10] + 7) = 17

…f[9] = max(f[9], f[5] + 7) = 17

f[8] = max(f[8], f[4] + 7) = 10;

看到沒,f[9]到f[14]的值重新整理了

i = 3 時

f[14] = max(f[14, f[8] + 14] = 24

f[13] = max(f[13], f[7] +14) = 24

…後面就不列舉了,我們要的結果f[14]已經出來了顯然這是正確的答案。

由此可見對於二維陣列,我們可以用一維滾動陣列來實現,減少不必要的浪費。

重點來了!!!用滾動陣列的時候內層迴圈一定要逆序

再寫一點關於初始化的問題,對於揹包問題,有要求揹包裝滿與不裝滿兩種情況

裝滿那就要f[0] = 0; 其他的都為-∞,為什麼呢,因為只有在揹包容量為0的時候才可以乙個都不裝價值為0,當揹包有容量,就可能裝得下東西,但你不能選擇什麼都不裝對吧,這不符合要求,所以初始化為-∞之後,只有當你裝之後這個值才有意義。

不要求裝滿就全部初始化為0,理由同上,裝與不裝是你的選擇,你可以留著容量不裝,這時所有值都有意義。

最後貼上01揹包的**

二維:

for

(int i =

1; i <= n; i++

)//n表示種類

for(

int j =

1; j <= v; j++

)//v表示揹包容量 這裡迴圈正序逆序都行

if(j >= c[i]

) f[i]

[j]=

max(f[i-1]

[j], f[i-1]

[j - c[i]

]+ v[i]

)else

f[i]

[j]= f[i-1]

[j] cout << f[n]

[v];

滾動陣列:

for

(int i =

1; i <= n; i++

)for

(int j = v; j >= c[i]

; j--

)// 一定要逆序

f[j]

=max

(f[j]

, f[j - c[i]

]+ v[i]);

cout << f[v]

;

動態規劃揹包問題 01揹包

問題描述 n種物品,每種乙個。第i種物品的體積為vi,重量為wi。選一些物品裝到容量為c的揹包,使得揹包內物品不超過c的前提下,重量最大。問題分析 宣告乙個f n c 的陣列。f i j 表示把前i件物品都裝到容量為j的揹包所獲得的最大重量。當 j v i 時,揹包容量不足以放下第 i 件物品,f ...

動態規劃 揹包問題 01揹包

有n種物品和乙個容量為v的揹包,每種物品僅用一次。第i件物品的費用是w i 價值是v i 求解將哪些物品裝入揹包可使價值總和最大。例如 n 5,v 10 重量 價值 第乙個物品 10 5 第二個物品 1 4 第三個物品 2 3 第四個物品 3 2 第五個物品 4 1 首先我們考慮貪心策略,選取最大價...

動態規劃 揹包問題(01揹包 完全揹包)

揹包問題 多種物品 重量不同 價值不同 你可以取最多重量不超過w的物品,問最大價值為多少?01揹包 指的是 有n個物品 每個物品的重量為w i 價值為v i 每個物品只有乙個 所有面臨這些物品只有兩種結果 1 拿這件物品,揹包容量減去w i 此時的價值增加v i 2 不拿這件物品,揹包容量不變,最大...