問題背景:
設有容量為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 不拿這件物品,揹包容量不變,最大...