從01揹包學習貪心演算法和動態規劃
阿新 • • 發佈:2019-01-02
從01揹包學習貪心演算法和動態規劃:
演算法的思路其實很大程度上都是相通的,比如在提升演算法執行時間的不斷探索中,我們用分治的思想來將一個大問題分解為很多小問題進行求解,並且這些子問題與原問題的結構是一樣的,比如歸併排序,比如第i層是排四個數,第i+1層則是排八個數,問題的規模發生變化但結構不變。而動態規劃則是沿用的分治的思想,但是比分治多兩個必要條件:重疊子問題和最優子結構。前者要求問題空間要足夠小,得到的遞迴演算法會反覆求解相同的子問題,而不是產生新的子問題,比如LCS和01揹包;而後者則比較晦澀,具體來說就是如果要找問題的最優解先確定該問題最優解包含了其子問題最優解,這樣就把責任一層層推下去(在程式中體現在直到遇見哨兵為止)。二貪心演算法則又是延續動態規劃,因為動態規劃自底向上計算總是要維護一個二維陣列記錄所有的子問題解,對於有些優化問題,沒有必要都計算。這時候我們可以使用自上而下的策略,做出一個選擇,然後求剩下的子問題,如果面臨兩個選擇,則選擇一個,繼續求解該子問題即可。
那麼接下來我們用01揹包問題具體說明這兩種演算法:
問題描述:
有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。基本思路這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的揹包可以獲得的最大價值。則其狀態轉移方程便是:
https://img-blog.csdn.net/20150501212932504
package DP;
import java.awt.*;
import java.util.Scanner;
public class bagdp {
static final int NIF = 9999;
static int N;//揹包數目
static int[] worth;//單個物品價值
static int[] cap;//單個物品容量
static int[][] values;//存放價值,也是自上而下求解問題的必備資料結構
static int maxVal;//最大價值
public static void main(String[] args){
Scanner in = new Scanner(System.in);
maxVal = in.nextInt();
N = in.nextInt();
worth = new int[N+1];
cap = new int[N+1];
values = new int[N+1][maxVal+1];
for(int i = 1;i <= N;i++){
worth[i] = in .nextInt();
cap[i] = in.nextInt();
}
for(int i = 0;i <= N;i++)
for(int j = 0;j <= maxVal;j++)
values[i][j] = NIF;
for(int i = 0;i <= N;i++)
values[i][0] = 0;
for(int i = 0;i <= maxVal;i++)
values[0][i] = 0;
//System.out.print("可裝最大價值為:"+greedy(N,maxVal)+"\n");
System.out.print("可裝最大價值為:"+greedy2()+"\n");
printBags();
in.close();
}
public static int greedy(int i,int j){//貪心演算法
if(values[i][j] < NIF) return values[i][j];//備忘錄
if(j < cap[i]) values[i][j] = greedy(i-1,j);//容不下就不要了
if(j >= cap[i]){//可以容得下
values[i][j] = max(greedy(i-1,j),greedy(i-1,j-cap[i])+worth[i]);
}
return values[i][j];
}
public static int greedy2(){//動態規劃演算法,自下而上
for(int i = 1;i <= N;i++)
for(int j = 1;j <= maxVal;j++)
{
if(j < cap[i]) values[i][j] = values[i-1][j];
else if(j >= cap[i]){
values[i][j] = max(values[i-1][j],values[i-1][j-cap[i]]+worth[i]);
}
}
return values[N][maxVal];
}
public static int max(int a,int b){
return a>b?a:b;
}
public static void printBags(){//回溯列印選擇的揹包
int j = maxVal;
for(int i = N;i > 0;i--){
if(values[i][j] > values[i-1][j]){
System.out.print("揹包"+i+" ");
j = j -cap[i];
if(j < 0) break;
}
}
}
}
greedy是貪心演算法(採用了備忘錄自上而下的思想),greedy2則採用動態規劃思想。