動態規劃:揹包問題
阿新 • • 發佈:2021-07-02
01 揹包
揹包問題的理論基礎是01揹包。
有N件物品和一個最多能被重量為W 的揹包。第i件物品的重量是weight[i],得到的價值是value[i] 。每件物品只能用一次,求解將哪些物品裝入揹包裡物品價值總和最大。
例子:
揹包最大重量為4。
物品 | 重量 | 價值 |
---|---|---|
物品0 | 1 | 15 |
物品1 | 3 | 20 |
物品2 | 4 | 30 |
問揹包能背的物品最大價值是多少?
動規五部曲
-
確定dp陣列以及下標的含義
對於揹包問題是使用二維陣列,即dp[i][j] 表示從下標為[0-i]的物品裡任意取,放進容量為j的揹包,價值總和最大是多少。
-
確定遞推公式
可以有兩個方向推出來dp[i][j],- 由dp[i - 1][j]推出,即揹包容量為j,裡面不放物品i的最大價值,此時dp[i][j]就是dp[i - 1][j]
- 由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 為揹包容量為j - weight[i]的時候不放物品i的最大價值,那麼dp[i - 1][j - weight[i]] + value[i] (物品i的價值),就是揹包放物品i得到的最大價值
所以遞迴公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
-
dp陣列如何初始化
首先如果揹包容量j為0的話,即dp[i][0],無論是選取哪些物品,揹包價值總和一定為0。
由狀態轉移方程可以看出i 是由 i-1 推匯出來,那麼i為0的時候就一定要初始化。dp[0][j]為存放編號0的物品的時候,各個容量的揹包所能存放的最大價值。
// 初始化需要倒敘遍歷,如果一旦正序遍歷了,那麼物品0就會被重複加入多次
for (int j = bagWeight; j >= weight[0]; j--) {
dp[0][j] = dp[0][j - weight[0]] + value[0]; // 初始化i為0時候的情況
}
- 確定遍歷順序
有兩個遍歷的維度:物品與揹包重量。兩者哪個先遍歷其實都可以,但是先遍歷物品更好理解。
先遍歷物品,然後遍歷揹包重量:
// weight陣列的大小 就是物品個數 for(int i = 1; i < weight.size(); i++) { // 遍歷物品 for(int j = 0; j <= bagWeight; j++) { // 遍歷揹包容量 if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 這個是為了展現dp數組裡元素的變化 else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); } }
先遍歷揹包,再遍歷物品:
// weight陣列的大小 就是物品個數
for(int j = 0; j <= bagWeight; j++) { // 遍歷揹包容量
for(int i = 1; i < weight.size(); i++) { // 遍歷物品
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
- 舉例推導dp陣列
來看一下對應的dp陣列的數值,如圖:
程式碼
public static void main(String[] args){
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
// 二維陣列
int[][] dp = new int[weight.length][bagWeight + 1];
// 初始化
for (int j = bagWeight; j >= weight[0]; j--) {
dp[0][j] = dp[0][j - weight[0]] + value[0];
}
// weight陣列的大小 就是物品個數
for(int i = 1; i < weight.length; i++) { // 遍歷物品
for(int j = 0; j <= bagWeight; j++) { // 遍歷揹包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
System.out.println(dp[weight.length - 1][bagWeight]);
}