1、01揹包問題數組
最基礎的揹包問題:有N件物品和一個容量爲V的揹包。第i件物品的體積是w[i],價值是v[i]。求解將哪些物品裝入揹包可以使物品的總價值達到最大。優化
咱們用dp[i][j]表示在整體積不超過j的狀況下,前i個物品所能達到的最大價值。初始時,dp[0][j]爲0。spa
依據每種物品是否被放入揹包,每一個狀態有兩個狀態轉移的來源。3d
1.若物品i被放入揹包,設其體積爲w,價值爲v,則dp[i][j] = dp[i-1][j-w]+v。code
2.若物品不放入揹包,則dp[i][j] = dp[i-1][j]。blog
選擇二者之中的較大值成爲狀態dp[i][j]的值。 即狀態轉移方程:dp[i][j] = max{ dp[i-1][j], dp[i-1][j-w]+v};class
同時注意:j-w的值是否爲非負值,若爲負值則該狀態來源不能被轉移。基礎
1 // 狀態轉移
for(int i=1; i<=m; i++) 2 {//循環每個物品 3 4 for(int j=t; j>=list[i].w; j--) 5 { 6 dp[i][j]=max(dp[i-1][j], dp[i-1][j-list[i].w]+list[i].v); 7 } 8 //j屬於list[i].w-1~0 9 for(int j=list[i].w-1; j>=0; j--) 10 { 11 dp[i][j]=dp[i-1][j]; 12 } 13 }
觀察狀態轉移的特色,咱們發現dp[i][j]的轉移僅與dp[i-1][j-list[i].w]和dp[i-1][j]有關,即僅與二維數組中本行的上一行有關。根據這個特色,咱們能夠將本來的二維優化。循環
dp[j] = max{ dp[j], dp[j-list[i].w]+v };遍歷
1 // 狀態轉移 (簡化後) 2 for(int i=1; i<=m; i++) 3 {//循環每個物品 4 5 for(int j=t; j>=list[i].w; j--) 6 { 7 dp[j]=max(dp[j], dp[j-list[i].w]+list[i].v); 8 } 9 10 }
在0-1揹包中,之因此逆序循環更新狀態是爲了保證更新dp[j]時,dp[j-list[i].w]的狀態還沒有由於本次更新而發生變化。所以必須逆序更新每一個dp[j]的值。
0-1揹包存在一個簡單的變化:即要求所選擇的物品必須剛好裝滿揹包。此時,只須要改變初始狀態,dp[0][0]爲0,而其餘dp[0][j]值均變化爲負無窮或者是不存在。該變化與原始的0-1揹包的差異只體如今初始值方面。
2、徹底揹包
徹底揹包是在0-1揹包的基礎上,使每種物品的數量無限增長。
徹底揹包:有一個體積爲V的揹包,同時又n個物品,每一個物品均有各自的體積w和價值v,每一個物品的數量均爲無限個,求使用該揹包最多能裝的物品價值總和。
簡單來講,使用以前空間優化過的一維數組來解,按以下方法轉移:
1 // 狀態轉移 (簡化後) 2 for(int i=1; i<=m; i++) 3 {//循環每個物品 4 5 for(int j=list[i].w; j<=t; j++) 6 { 7 dp[j]=max(dp[j], dp[j-list[i].w]+list[i].v); 8 } 9 10 }
與0-1揹包相比,彷佛只存在着對狀態j的遍歷順序有所差別。這是由於0-1揹包中每一個物品至多隻能被選擇一次。而在徹底揹包中,每一個物品能夠被選擇無限次,那麼狀態dp[i][j]剛好能夠由可能已經放入物品i的狀態轉移而來。
總結一下:徹底揹包解法與0-1揹包總體保持一致。不一樣的只是狀態更新時的遍歷順序。