2個問題:
1)背包問題的動態規劃解法
2)動態規劃的另一種實現方式,記憶遞迴(dp的第一篇文章就提到過,professional中也提到過),在這裡來講解,加上全面所有的文章,這一點應該可以算是動態規
劃裡最後一個沒有詳細介紹的關鍵點了
---------------------------------------------------------------------------------------------------------------------------------------------------
給定一組物品:
重量為 w1 , w2 , .......wn
價值為 v1 , v2 , .........vn
和一個稱重量為W的書包。
求這些物品的一個最有價值的子集,可以裝到書包中去。
--------------------------------------------------------------------------------------------------------------------------------------
1,背包問題
設 V[ i , j ]表示能夠放進稱重量為 j 的背包的前 i 個物品的最有價值子集的價值,目標是求V[ n , W ]
根據 V[ i , j ] 的最佳子集中是否包含物品 i,可以得到下列遞推式:
這個遞推式的意思是:如果 i 的重量已經比 j 大了,那顯然不含 i ( j-wi < 0 ), 如果 i 的重量比 j 小,那麼可能包含 i (如果包含能去到最大值的話)。
這個表可以按行也可以按列填:
實現:
package Section8;
/*第八章 動態規劃 背包問題*/
public class BackPack {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] w = { 2, 1, 3, 2 }; // 重量數組
int[] v = { 12, 10, 20, 15 }; // 價值數組
int W = 5;
int[][] result = backPack(w, v, W);
for (int i = 0; i < result.length; i++)
{
for (int j = 0; j < result[0].length; j++)
System.out.print(result[i][j] + " ");
System.out.println();
}
}
public static int[][] backPack(int[] w, int[] v, int W) {
// w是物品重量數組,v事物品價值數組,W是背包重量
// 返回表達背包問題求解過程的矩陣
int n = w.length; // w和v的長度是相同的
int[][] result = new int[n + 1][W + 1]; // 前i個物品(i從0到n),W從0到W
// 初始條件:result[0][j] = 0;result[i][0] = 0;
for (int i = 0; i <= W; i++)
result[0][i] = 0;
for (int i = 0; i <= n; i++)
result[i][0] = 0;
// 根據動態規劃的狀態轉移方程填表:這個表格可以一行一行的填,也可以一列一列的填,這裡採用一行一行的填
// 注意填表方式是動態規劃裡面非常重要的一個東西,當你填某一個位置時,它需要用到的其他位置必須都已經填好
// 所以填表的方式是跟狀態轉移方程相關滴,深層次來說,是跟動態規劃構造解的產生過程相關的
for (int i = 1; i <= n; i++) // 行數從1到n
{
for (int j = 1; j <= W; j++) // 列數從1到W
{
// 此時決定了一個i,j的位置要填:result[i][j]
if (j - w[i - 1] < 0)
result[i][j] = result[i - 1][j];
else
result[i][j] = max(result[i - 1][j], v[i - 1] + result[i - 1][j - w[i - 1]]);
}
}
return result;
}
public static int max(int m, int n) {
if (m >= n)
return m;
return n;
}
}
結果:
0 0 0 0 0 0
0 0 12 12 12 12
0 10 12 22 22 22
0 10 12 22 30 32
0 10 15 25 30 37
寫了這麼幾個動態規劃,已經可以發現,有了遞推式後,其編程就顯得非常easy,基本上就是照著公式操作矩陣。
因此,應重點關注動態規劃的思想,怎麼去描述和刻畫問題,發現最有子結構,得到其狀態轉移方程。
-------------------------------------------------------------------------------------------------------------------------------------------------
2,記憶遞迴--動態規劃的另一種實現方式
關於什麼什麼是記憶遞迴,它的出發點是什麼,為什麼要記憶遞迴,這些在前面都提過,這裡再簡單說下:
動態規劃的核心思想之一就是記憶(或者記錄)來避免對重複子問題進行求解,然而,它並沒有避免對不必要子問題進行求解。
若將遞迴與動態規劃結合起來,就可以得到一種動態規劃的遞迴實現方式,這種方式避免了對不必要子問題進行求解。
實際上就是在遞迴的時候,在動態規劃表中先檢查要遞迴的項是否已經求出來了,如果已經求出來了,就直接返回答案,否則才去遞迴求解。
背包問題的記憶遞迴實現:
實際上,就是在V[ i , j]還沒有求出來的時候(一旦一個V[ i , j]求出來了,就不會再變,這點很重要),才去遞迴,否則就直接傳回值。
想想斐波拉契數列也可以記憶遞迴的去實現。
-------------------------------------------------------------------------------------------------------------------------------------------------
總結:
1) 背包問題的時間複雜度和空間複雜度都是 nW,2個迴圈,見代碼
2)一個關鍵點:動態規劃的另一種實現方式,記憶遞迴