算法设计与分析 0-1背包问题 动态规划解法【超详细】
0-1背包問題 問題描述
給定i個物品和一個容量為的背包,物品的重量是Wi,其價值為Vi
物品個數為i,背包容量為C。
如何選擇裝入背包內的物品,使得裝入背包中的物品的總價值最大?
其中,每種物品只有全部裝入背包或不裝入背包兩種選擇。
物品不能分割,不能重復使用。
動態規劃算法思路
首先建立一個數組B[i][c],最上面一行表示背包容量,最左邊一列表示物品編號,中間填充的數值表示當當前背包容量為C,當前被考慮的物品編號為k的情況下,做出最優決定時產生的背包價值。
如下圖所示:(不用關心圖中數據,理解整體結構含義即可)
實例分析
已知背包的容量為20,并給定5種物品,其重量和價值如下圖所示:
令B(k,w)表示在前k個物品中能夠裝入容量為的背包中時,背包總價值的最大值。
得到如下動態函數:
利用上述函數,一行一行填表即可。
填表順序為:先從左上角開始,填第一行,第二行,…一直到結束,并不需要遞歸。
右下角即背包可裝下的最大價值26
詳細分析 疑難解答
可能有人會問,“當判斷一個物品k是否應該裝入背包的時候,難道不是只要能裝進去,就應該裝進去,這樣才是最優嗎”?
對于這個問題,我的理解是:
不一定非要裝進去當前能裝進去的物品。如果當前物品能裝進去,那么:
- 放入第k件物品后,背包總價值 = 先給這件物品留出空間,剩余的背包大小能裝進的最大價值 + 這件物品的價值
- 不放入第k件物品,背包總價值 = 不用給這件物品留出空間,當前背包大小能裝進的最大價值(就是判斷完上一件物品之后背包的價值)
說白了,就是當當前物品又大又輕時,雖然可以把它裝進去,但是在給它留出空間的同時,浪費了背包的容量。還不如不裝當前物品,而去選擇其他物品裝入。
那么這個“其他物品”指什么呢?就是在判斷這個物品該不該裝之前,已經計算過的當前背包容量下,可以將前k-1件物品放入或不放入時,背包的最大價值。
如下圖,這是某一個物品“不放”比“放”好的例子:
用C代碼實現后,應該更容易理解一些(不懂的話請看注釋,重點在注釋):
//填表過程for (k = 1; k < N; k++){for (C = 1; C < W; C++){if (w[k] > C) //第k件物品放不進去 此時背包的價值 = 判斷完上一件物品之后背包的價值{B[k][C] = B[k - 1][C];}else{int value1 = B[k - 1][C - w[k]]+v[k]; //放入第k件物品后 背包總價值 = 先給這件物品留出空間,剩余的背包大小能裝進的最大價值 + 這件物品的價值int value2 = B[k - 1][C]; //不放入第k件物品 背包總價值 = 不用給這件物品留出空間,當前背包大小能裝進的最大價值(就是判斷完上一件物品之后背包的價值)if (value1 > value2){B[k][C] = value1;}else{B[k][C] = value2;}}}}逆推裝入的物品
計算出矩陣以及最大可裝價值之后,如何逆推裝入的物品?
如下圖,從右下角開始,向上層層逆推。
- 如果上下數字相同,說明這個物品未被放入背包。
- 如果上下數字不同,說明這個物品已被放入背包,此時計算放入此物品之前背包剩余容量,并找出上一行對應位置。
附錄
我的筆記
運行結果
前幾行輸出為:某一個物品“不放”比“放”好的情況,可以無視
后面的矩陣才是最終的運算結果
完整代碼 C++
#include<iostream> #include<stdio.h> #define N 6 //物品的個數 #define W 21 //背包容量int B[N][W] = { 0 }; int w[6] = { 0,2,3,4,5,9 }; //物品重量 int v[6] = { 0,3,4,5,8,10 };//物品價值void knapsack() {int k; //第K個物品int C; //背包剩余重量//填表for (k = 1; k < N; k++){for (C = 1; C < W; C++){if (w[k] > C) //第k件物品放不進去 此時背包的價值 = 判斷完上一件物品之后背包的價值{B[k][C] = B[k - 1][C];}else{int value1 = B[k - 1][C - w[k]] + v[k]; //放入第k件物品后 背包總價值 = 先給這件物品留出空間,剩余的背包大小能裝進的最大價值 + 這件物品的價值int value2 = B[k - 1][C]; //不放入第k件物品 背包總價值 = 不用給這件物品留出空間,當前背包大小能裝進的最大價值(就是判斷完上一件物品之后背包的價值)if (value1 > value2){B[k][C] = value1;}else{B[k][C] = value2;if (value1 < value2)printf("k=%d C=%d\n", k, C);}}}} } int main() {knapsack();for (int i = 0; i < N; i++){for (int j = 0; j < W; j++){printf("%4d ", B[i][j]);}printf("\n\n");}system("pause"); }總結
以上是生活随笔為你收集整理的算法设计与分析 0-1背包问题 动态规划解法【超详细】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法设计与分析 自创O(n)排序算法 适
- 下一篇: 算法设计与分析 矩阵连乘问题 动态规划