演算法(Java實現)—— 動態規劃演算法
阿新 • • 發佈:2020-12-19
動態規劃演算法
應用場景—0-1揹包問題
揹包問題:有一個揹包,容量為4磅,現有物品如下
物品 | 重量 | 價格 |
---|---|---|
吉他(G) | 1 | 1500 |
音響(S) | 4 | 3000 |
電腦(L) | 3 | 2000 |
要求:
-
達到目標為裝入的揹包的總價值最大,且重量不超出
-
要求裝入的物品不可重複
動態規劃演算法介紹
-
動態規劃(Dynamic Programming)演算法的核心思想是:將大問題劃分為小問題進行解決,熊二一步步獲取最優解的處理演算法
-
與分治演算法類似,但不同的是動態規劃子問題不相互獨立
-
動態規劃可以通過填表的方式來逐步推進,得到最優解
解決0-1揹包問題
主要思想
利用動態規劃來解決,每次遍歷到第i個物品,根據w[i]和v[i]來確定是否需要將該物品放入揹包中。即對於給定的n個物品,令:
-
w[i]:第i個商品的重量
-
val[i]:第i個商品的價值
-
C:揹包容量
-
v[i][j :表示前i個物品中能夠裝入容量為j的揹包中的最大價值
則有下列結論:
v[i][0] = v[0][j] = 0;
當w[i] > j時:v[i][j] = v[i-1][j]
當j >= w[i]時:v[i][j] = max{v[i-1][j],v[i-1][j-w[i]] + val[i]}
思路圖解
揹包的填表過程
-
物品還未裝入揹包,初始狀態
行,0磅,1磅……代表揹包容量,哪一行表示可以放入此行及 以上行的物品,但是哪一行先方哪一行的物品
列,代表物品在對應揹包容量下各自在揹包中的價格
物品 0磅 1磅 2磅 3磅 4磅 0 0 0 0 0 吉他(G) 0 音響(S) 0 電腦(L) 0 -
加入現在只有吉他此時不論揹包容量有多大,只能放一把吉他
物品 0磅 1磅 2磅 3磅 4磅 0 0 0 0 0 吉他(G) 0 1500(G) 1500(G) 1500(G) 1500(G) 音響(S) 0 電腦(L) 0 -
假如有吉他和音響,當揹包容量同時滿足多個物品時,考慮哪個物品價值更高將其放入
物品 0磅 1磅 2磅 3磅 4磅 0 0 0 0 0 吉他(G) 0 1500(G) 1500(G) 1500(G) 1500(G) 音響(S) 0 1500(G) 1500(G) 1500(G) 3000(S) 電腦(L) 0 -
假如由吉他,音響,電腦時,先放電腦,放完之後如果有空餘空間可以放入其他物品則放入,否則不用再關心
物品 0磅 1磅 2磅 3磅 4磅 0 0 0 0 0 吉他(G) 0 1500(G) 1500(G) 1500(G) 1500(G) 音響(S) 0 1500(G) 1500(G) 1500(G) 3000(S) 電腦(L) 0 1500(G) 1500(G) 2000(L) 2000(L) + 1500(G) 則有下列結論:
//表示填入的表的第一行和第一列置0
v[i][0] = v[0][j] = 0;
//當新增加商品時,若新商品大於揹包容量時,則直接使用上一單元格的裝入策略
當w[i] > j時:v[i][j] = v[i-1][j]
//當新增加商品時,其容量小於揹包容量,
//裝入的策略:
//1. v[i-1][j]上一單元格的價值
//2. v[i-1][j-w[i]] + v[i]當前商品的 價值+剩餘空間裝入物品價值的最大 值
//3. 此時比較裝入商品的價值,使用價值最 大的策略
當j >= w[i]時:v[i][j] = max{v[i-1][j],v[i-1][j-w[i]] + val[i]}
程式碼實現
package whyAlgorithm.dynamic;
import java.util.Arrays;
/**
* @Description TODO 動態規劃解決0-1揹包問題
* @Author why
* @Date 2020/12/9 21:04
* Version 1.0
**/
public class KnapsackProblem {
public static void main(String[] args) {
int[] w = {1,4,3};//物品重量
int[] val = {1500,2000,3000};//物品價值
int m = 4;//揹包容量
int n = val.length;//物品個數
//為記錄放入商品的情況,定義一個二維陣列
int[][] path = new int[n+1][m+1];
//建立二維陣列
//v[i][j]表示前i個物品能夠裝入容量為j的揹包中最大的價值
int[][] v = new int[n+1][m+1];
//初始化第一行和第一列,在本程式中可以不處理,因為預設為0
for (int i = 0; i < v.length; i++) {
//將第一列置為0
v[i][0] = 0;
//將第一行置為0
v[0][i] = 0;
}
//根據前面的公式動態規劃處理
for (int i = 1; i < v.length; i++) {//不處理第一行
for (int j = 1; j < v[0].length; j++) {//不出來第一列
//公式
//因為i從1開始,故原公式修改為 w[i] = w[i-1]
if (w[i-1] > j){
v[i][j] = v[i-1][j];
}else {
//int b = v[i-1][j-w[i-1]] + val[i-1];
//int max = Math.max(v[i - 1][j], b);
//v[i][j] = max;
//為了記錄商品存放到揹包的情況不能簡單地使用上面的公式,需要使用if,else體現這個公式
if (v[i-1][j] < v[i-1][j-w[i-1]] + val[i-1]){
v[i][j] = v[i-1][j-w[i-1]] + val[i-1];
//記錄當前情況
path[i][j] = 1;
}else {