在使用動態規劃思想解決的問題中,最常見的是揹包的問題,而在揹包問題中,最簡單的就是0-1揹包。
0-1揹包的問題形式如下:有一揹包,只能裝重量爲V的物品,有n個的物品,這些物體體積爲w,價值爲v,在每個物品最多裝一個的情況下,怎麼裝物品可以讓揹包中的物品價值最大。該問題之所以可以用動態規劃來解決,是由於將“大問題”拆解後的“小問題”存在着依賴關係,並不獨立,這也是動態規劃和分治最大的區別。
在對該問題進行分析之前,可以先定義一些變量,來輔助分析。具體爲:value[ i ]表示第i個物品的價值,weight[ i ]表示第i個物品的體積,dp[ i ][ j ]表示當前揹包容量爲j時,前i個物品最佳組合對應的價值。爲方便分析,設定物品個數爲4,揹包重量限制爲8,示例數據如下:
體積(weight) | 2 | 3 | 4 | 5 |
價值(value) | 3 | 4 | 5 | 6 |
分析步驟如下:
1、構建一張動態規劃表dp,表的大小爲(物品個數+1)*(揹包重量限制+1)。dp[0][0]-dp[0][物品+1]和dp[0][0]-dp[0][揹包重量限制+1]全爲0,如下:
個數/重量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | ||||||||
2 | 0 | ||||||||
3 | 0 | ||||||||
4 | 0 |
2、以dp[ i ][ j ],即揹包可裝重量爲j,第i個物品的狀態來進行分析。此時可能進行的動作有如下幾種:
1>要裝入物品的重量比揹包可裝的重量要大,此時不能再裝入新物品,即dp[ i ][ j ] = dp[i - 1][ j ];
2>此時能裝下新物品,但具體要不要裝入則需要看是否能達到最大價值,如果達不到,則和不能裝入效果一樣,即 dp[ i ][ j ] = dp[i - 1][ j ]。如果能裝入,則dp[i - 1][j - weight[ i ]] + value[ i ]。
尋找最大價值的過程,就是一個逐行不斷填表的過程,最終填表結果如下:
物品/重量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 |
4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 |
Java代碼如下:
int[ ] weight = {0,2,3,4,5}; //商品的重量2、3、4、5
int[ ] value = {0,3,4,5,6}; //商品的價值3、4、5、6
int bagCap = 8; //揹包承受的重量限制
int[ ][ ] dp = new int[weight.length][bagCap+1]; //動態規劃表
//構建動態規劃表
for (int i = 1; i <= weight.length-1; i++) {
for (int j = 1; j <= bagCap; 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 ]);
}
}
//動態規劃表的輸出
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(dp[ i ][ j ]+" ");
}
System.out.print("\n");
}