題目
有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的費用是w[i],價值是v[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。
基本思路
這個問題非常類似於01揹包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已並非取或不取兩種,而是有取0件、取1件、取2件……等很多種。如果仍然按照解01揹包時的思路,令dp[i][j]表示前i種物品恰放入一個容量爲v的揹包的最大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程,像這樣:
完全揹包的模板代碼:
//N爲物品數量
//W爲揹包總體積
//weights[]爲每個物品的體積
//values[]爲每個物品的價值
public int completeKnapsack(int N, int W, int[] weights, int[] values){
int[] dp = new int[W+1];
for(int i = 1; i <= N; i++){
int w = weights[i-1], v = values[i-1];//每個物品的體積和價值
for(int j = W; j >= w; j--){ //逆序
for(int k = 0; k * w <= j; k++){
dp[j] = Math.max(dp[j], dp[j-k*w]+k*v);
}
}
}
return dp[W];
}
那麼我們只用一維數組記錄,空間優化後的狀態轉移方程:
完全揹包空間優化模板:
//N爲物品數量
//W爲揹包總體積
//weights[]爲每個物品的體積
//values[]爲每個物品的價值
public int completeKnapsack(int N, int W, int[] weights, int[] values){
int[] dp = new int[W+1];
for(int i = 1; i <= N; i++){
int w = weights[i-1], v = values[i-1];//每個物品的體積和價值
for(int j = w; j <= W; j++){ //順序
dp[j] = Math.max(dp[j], dp[j-w]+v);
}
}
return dp[W];
}
注意:完全揹包的兩層for循環的次序是可以顛倒的。
如何理解空間優化模板中揹包容量需要順序遍歷(對比01揹包中的逆序遍歷)?
對於轉移方程dp[j] = Math.max(dp[j], dp[j-w]+v), 揹包容量順序遍歷的話,dp[i-1][j-v]會被dp[i][j-v]所覆蓋。所以,
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-w]+v).理解,
特別注意,dp[i][j-w]+v代表着是前i個物品體積爲j-w的最優解,再放入一個第i個物品的最優解。而dp[i][j-w]中可能已經有0, 1, 2, 3...個第i個物品。
舉個例子:
N = 3, W = 3
dp[3][4] = Math.max(dp[2][4], dp[3][3]+2) = 8
總結:
01揹包,揹包容量逆序遍歷,dp[j-w]代表dp[i-1][j-w];
完全揹包,揹包容量順序遍歷,dp[j-w]代表dp[i][j-w];
推薦:
揹包九講1——01揹包問題的理解(Java圖解)
https://blog.csdn.net/caigen0001/article/details/106698380
揹包九講2——完全揹包問題的理解(Java圖解)
https://blog.csdn.net/caigen0001/article/details/106711469
揹包九講3——多重揹包問題的理解(Java圖解)
https://blog.csdn.net/caigen0001/article/details/106720118
揹包九講4——二維揹包問題的理解(Java圖解)
https://blog.csdn.net/caigen0001/article/details/106720280
參考資料:
揹包九講 https://github.com/tianyicui/pack
揹包九講專題 https://www.bilibili.com/video/BV1qt411Z7nE?from=search&seid=6165804124910947817