1. 程式人生 > >動態規劃之0-1揹包問題,鋼條切割

動態規劃之0-1揹包問題,鋼條切割

動態規劃

首先說說動態規劃:動態規劃與分治法相似,都是組合子問題的解來解決原問題的解,與分治法的不同在於:分治法的子問題是相互獨立存在的,而動態規劃應用於子問題重疊的情況
設計動態規劃演算法的步驟:
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;
    }