動態規劃之 0-1 揹包問題詳解
個人部落格:DoubleFJ の Blog
前言
揹包問題是比較經典的動態規劃演算法題,之前沒接觸過演算法都沒聽說過這個,也是後來在 leetcode 中刷題時才瞭解到,慚愧慚愧啊。演算法的世界太奇妙,數學一直都是那麼令人著迷。今天來總結一下這個 01 揹包問題。注:這裡的物品不可拆分。
動態規劃
首先了解下什麼是動態規劃。動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep decision process)的優化問題時,提出了著名的最優化原理(principle of optimality),把多階段過程轉化為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法——動態規劃。1957年出版了他的名著《Dynamic Programming》,這是該領域的第一本著作。
動態規劃演算法通常用於求解具有某種最優性質的問題。在這類問題中,可能會有許多可行解。每一個解都對應於一個值,我們希望找到具有最優值的解。動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。與分治法不同的是,適合於用動態規劃求解的問題,經分解得到子問題往往不是互相獨立的。若用分治法來解這類問題,則分解得到的子問題數目太多,有些子問題被重複計算了很多次。如果我們能夠儲存已解決的子問題的答案,而在需要時再找出已求得的答案,這樣就可以避免大量的重複計算,節省時間。我們可以用一個表來記錄所有已解的子問題的答案。不管該子問題以後是否被用到,只要它被計算過,就將其結果填入表中。這就是動態規劃法的基本思路。具體的動態規劃演算法多種多樣,但它們具有相同的填表格式。
詳情可見:動態規劃
問題詳解
問題描述
給定 N 種物品和一個揹包。物品i的重量是 Wi,其價值位 Vi,揹包的容量為 C。問應該如何選擇裝入揹包的物品,使得轉入揹包的物品的總價值為最大。
問題分析
在選擇物品的時候,對每種物品只有兩種選擇,要麼裝入,要麼不裝入。因此,此為一個 0-1 揹包問題。
01 揹包的遞迴公式為:
m[i,0] = m[0,j] = 0
m[i,j] = m[i-1,j] ,j < wi
m[i,j] = max(m[i-1,j-wi]+vi, m[i-1,j]) ,j >= wi
其中,m[i,j]為前 i 件物品中選擇若干件,放入承重為 j 的揹包中,得到的最大的價值。
wi 為第 i 件商品的重量。
vi 為第 i 件商品的價值。
例題講解
有編號為 a,b,c,d,e 的五件物品,他們的重量分別為 4,5,6,2,2,價值分別為 6,4,5,3,6,現在給你一個承重為 10 的揹包,怎麼實現價值最大。
根據上述公式可以得到一個資料表,表從上向下生成:
name | weight | value | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
a | 4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
b | 5 | 4 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
c | 6 | 5 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 11 |
d | 2 | 3 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
e | 2 | 6 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
故可以根據公式碼出如下實現程式碼:
public static void main(String[] args) {
int n = 5;// 5件物品,物品編號為a,b,c,d,e(下面為多加一件物品,第一個物品為虛擬的物品)
int weight[] = { 0, 4, 5, 6, 2, 2 };// 物品的重量
int value[] = { 0, 6, 4, 5, 3, 6 }; // 對應物品的價值
int c = 10; // 揹包容量
int state[] = { 0, 0, 0, 0, 0, 0 };// 開始狀態
char name[] = { ' ', 'a', 'b', 'c', 'd', 'e' };
int maxValue = getMaxValue(n, weight, value, state, c);
System.out.println("最大價值為 = " + maxValue);
System.out.print("放入的物品為 :");
for (int i = 1; i <= 5; i++) {
if (state[i] == 1) {
System.out.print(name[i] + " ");
}
}
// System.out.println();
}
/**
*
* @param n
* 物品數量
* @param weight
* 物品對應重量(陣列下標從0開始,故第一個物品為虛擬物品)
* @param value
* 物品對應價值(陣列下標從0開始,故第一個物品為虛擬物品)
* @param state
* 物品的開始狀態
* @param c
* 揹包的容量
* @return
*/
public static int getMaxValue(int n, int weight[], int value[], int state[], int c) {
// n 為物品的數量,陣列時需要加 1,此時可以從 0,1,...n 個物品,共 n+1 個商品,其中第 0 個為虛構物品
// 對於物品的價值,可以寫成 2 維陣列
int m[][] = new int[n + 1][c + 1]; // n 為 0,1,2...(n-1),揹包重量為 0,1,2...C
int i, j;
for (i = 0; i <= n; i++) {
m[i][0] = 0;
}
for (j = 0; j <= c; j++) {
m[0][j] = 0;
}
for (i = 1; i <= n; i++) {
// System.out.println();
for (j = 1; j <= c; j++) {
if (j < weight[i]) { // 新的物品太重,無法放下
m[i][j] = m[i - 1][j];
} else {// 分為放和不放 取較大值
m[i][j] = Math.max(m[i - 1][j - weight[i]] + value[i], m[i - 1][j]);
}
// System.out.print("m["+i+"]["+j+"]="+m[i][j]+" ");
// System.out.print(m[i][j]+" ");
}
}
// 根據其最大價值,反向推斷是否新增了物品 i
j = c;
for (i = n; i > 0; i--) {
if (m[i][j] > m[i - 1][j]) {// 物品 i 新增到了序列列表
state[i] = 1;
j = j - weight[i];
} else { // 沒有新增
state[i] = 0;
}
}
return m[n][c]; // 最大價值
}
輸出結果為:
最大價值為 = 15
放入的物品為 :a d e
具體實現看上述程式碼即可,註釋齊全,簡單易懂。
參考資料
相關文章
- 動態規劃解0-1揹包問題動態規劃
- 0-1揹包問題(動態規劃)動態規劃
- 【動態規劃】0-1揹包問題原理和實現動態規劃
- 【動態規劃】揹包問題動態規劃
- 揹包問題----動態規劃動態規劃
- 詳解動態規劃01揹包問題--JavaScript實現動態規劃JavaScript
- 詳解動態規劃01揹包問題–JavaScript實現動態規劃JavaScript
- 動態規劃 01揹包問題動態規劃
- 【動態規劃】01揹包問題動態規劃
- 動態規劃--01揹包問題動態規劃
- 動態規劃篇——揹包問題動態規劃
- 動態規劃-01揹包問題動態規劃
- 動態規劃之0,1揹包問題動態規劃
- 動態規劃系列之六01揹包問題動態規劃
- 【動態規劃】01揹包問題【續】動態規劃
- leetcode題解(0-1揹包問題)LeetCode
- 動態規劃-揹包01問題推理與實踐動態規劃
- 01揹包問題理解動態規劃演算法動態規劃演算法
- 【動態規劃】一次搞定三種揹包問題動態規劃
- 揹包問題演算法全解析:動態規劃和貪心演算法詳解演算法動態規劃
- python 動態規劃(揹包問題和最長公共子串)Python動態規劃
- 整數0-1揹包問題
- 前端與演算法-動態規劃之01揹包問題淺析與實現前端演算法動態規劃
- 演算法-動態規劃-完全揹包演算法動態規劃
- 【演算法】0-1揹包問題演算法
- 01揹包動態規劃空間優化動態規劃優化
- 動態規劃之股票問題123動態規劃
- 多重揹包動態規劃及空間優化動態規劃優化
- 【LeetCode動態規劃#08】完全揹包問題實戰與分析(零錢兌換II)LeetCode動態規劃
- 【LeetCode動態規劃#06】分割等和子集(01揹包問題一維寫法實戰)LeetCode動態規劃
- 動態規劃之 KMP 演算法詳解動態規劃KMP演算法
- 01揹包、完全揹包、多重揹包詳解
- leetcode題解(動態規劃)LeetCode動態規劃
- 動態規劃解題方法動態規劃
- 詳解動態規劃最少硬幣找零問題–JavaScript實現動態規劃JavaScript
- 詳解動態規劃最少硬幣找零問題--JavaScript實現動態規劃JavaScript
- Leetcode 題解演算法之動態規劃LeetCode演算法動態規劃
- 庖丁解牛斐波拉契數列和揹包問題——詳細解析兩個問題優化過程,帶你從最基本的問題看懂動態規劃!!!優化動態規劃