動態規劃之0-1揹包問題,鋼條切割
阿新 • • 發佈:2019-01-03
動態規劃
首先說說動態規劃:動態規劃與分治法相似,都是組合子問題的解來解決原問題的解,與分治法的不同在於:分治法的子問題是相互獨立存在的,而動態規劃應用於子問題重疊的情況。
設計動態規劃演算法的步驟:
1、刻畫一個最優解的結構特徵
2、遞迴地定義最優解的值
3、計算最優解的值,通常採用自底向上的方法
4、利用算出的資訊構造一個最優解
0-1揹包問題
例題:假設現有容量10kg的揹包,另外有3個物品,分別為a1,a2,a3。物品a1重量為3kg,價值為4;物品a2重量為4kg,價值為5;物品a3重量為5kg,價值為6。將哪些物品放入揹包可使得揹包中的總價值最大?
public static void main(String[] args) {
int w[] = {3, 4, 5};
int p[] = {4, 5, 6};
int m = 10;
int n = 3;
int c[][] = beibao(m,n,w,p);
for (int i = 0; i <=n; i++) {
for (int j = 0; j <=m; j++) {
System.out.print(c[i][j]+"\t");
if (j==m){
System.out.println();
}
}
}
}
/**
*
* @param m 揹包的最大承重量
* @param n 物品的個數
* @param w 物品的重量集合
* @param p 物品的價值集合
* @return 二維陣列 代表 對應的商品數量和揹包重量 的最大價值
* 說明:
* w[i] : 第i+1個物體的重量;
* p[i] : 第i+1個物體的價值;
* c[i][m] : 前i個物體放入容量為m的揹包的最大價值;
* c[i-1][m] : 前i-1個物體放入容量為m的揹包的最大價值;
* c[i-1][m-w[i]] : 前i-1個物體放入容量為m-w[i]的揹包的最大價值;
* 取最優解公式:
* c[i][m]=max{c[i-1][m-w[i]]+pi , c[i-1][m]} 根據前面的最優解 取得當前的最優解
*/
public static int[][] beibao(int m,int n,int[] w,int[] p){
// 定義最優解集合: n=3 m=10 而二維陣列c的下標為 i =[0-3] j=[0-10]
int[][] c = new int[n+1][m+1];
//初始化 二維陣列c 第1列的值為0 代表 當 揹包的最大重量為0時 前 i個物品的最大價值
for(int i=0;i<n+1;i++){
c[i][0] = 0;
}
//初始化 二維陣列c 第一行的值為0 代表 當 前0個物品 揹包最大重量為j 的最大價值
for(int j=0;j<m+1;j++){
c[0][j] = 0;
}
// 迴圈前 1-3個物品
for(int i=1;i<n+1;i++){
//迴圈 前i個物品下的揹包最大重量 1-10
for(int j=1;j<m+1;j++){
//step1:當 揹包的最大重量 大於等於 第i個物品的重量 時 說明能放下第i個物品
if (w[i - 1] <= j) {
//step2:判斷前c[i - 1][j]的最優解 是否小於 c[i - 1][j - w[i - 1]] + p[i - 1]的值
if (c[i - 1][j] < (c[i - 1][j - w[i - 1]] + p[i - 1])) {
c[i][j] = c[i - 1][j - w[i - 1]] + p[i - 1];
}else {
c[i][j] = c[i - 1][j];
}
} else {
c[i][j] = c[i - 1][j];
}
}
}
return c;
}
結果圖:
鋼條切割問題
例題:給定一段長度為n英寸的鋼條和一個價格表 pi (i=1,2, …,n),求切割鋼條的方案,使得銷售收益rn最大。注意,如果長度為n英寸的鋼條價格pn足夠大,最優解可能就是完全不需要切割。
若鋼條的長度為i,則鋼條的價格為Pi,如何對給定長度的鋼條進行切割能得到最大收益?
長度i 1 2 3 4 5 6 7 8 9 10
價格Pi 1 5 8 9 10 17 17 20 14 30
public static void main(String[] args) {
int []p = {-1,1, 5, 8, 9, 10, 17, 17, 20, 14, 30};
System.out.println(toDownToUp(p,7));
}
/**
*
* @param p 鋸條價格列表
* @param n 鋸條長度
* @return r陣列 長度為[0-n]的最優解
*/
public static int toDownToUp(int[] p ,int n){
int[] r = new int[n+1];
for (int i = 0; i < r.length; i++) {
r[i] = 0;
}
return downToUp(p,n,r);
}
/**
* 自底向上刻畫最優解
* @param p 鋸條價格列表
* @param n 鋸條長度
* @param r 最優解列表
* @return
*
* 說明:
* 從1-n 逐一刻畫Rn的最優解
* 刻畫最優解是依賴於已經刻畫過的最優解進行分析
*/
public static int downToUp(int[] p ,int n,int[] r){
//迴圈長度[1-n] 刻畫最[1-n]的最優解
for(int i=1;i<=n;i++){
//初始化定義 i 長度的最優解
int q = -9999;
//迴圈 i 長度的所有 分割方案
for(int j=1;j<=i;j++){
//取i長度的所有方案中的最優解
q = max(q,p[j]+r[i-j]);
}
//賦值到最優解集合中。
r[i] = q;
}
return r[n];
}
public static int max(int a,int b){
return a>b?a:b;
}