動態規劃-揹包問題

// 最優原則:無論前面的策略如何,此後的決策是是基於當前狀態(由上一次決策產生)的最優決策。
// 當最優決策序列中包含最優決策子序列時,可創建動態規劃遞歸方法。
// (有些問題的遞歸式不必定能保證最優原則,所以在求解時有必要對它進行驗證。若不能保持最優原則,則不可應用動態規劃方法。)
// 在獲得最優解的遞歸式以後,須要執行回溯以構造最優解。
// 缺點:若是不努力地去避免重複計算,遞歸程序的複雜性將很是可觀。
// 方案:若是在遞歸程序中解決了重複計算問題時,複雜性將急劇降低。
// 遞歸方程也可用迭代方程來求解,這很天然地避免了重複計算。迭代方程雖然具備相同的複雜性,不須要附加的遞歸棧空間,所以更快一些。html

參考博客:算法

一、動態規劃之01揹包問題(最易理解的講解)數組

二、0-1揹包問題--動態規劃算法spa

三、揹包問題  遞歸方法.net

問題描述:
   給定n種物品和一揹包,物品i的重量是wi,其價值爲vi,揹包的容量爲C。問應如何選擇裝入揹包的物品(物品不能分割),使得裝入揹包中物品的總價值最大?code

分析:
  設f(i,j)爲裝入揹包中的最大總價值,其中i是前i個物品,j是指揹包剩餘的承重,不是總承重
  一、把這個問題抽象成找到最優數組問題,x[0],x[1],x[2],x[3].....是一個01序列,其中1表示裝入,0表示不裝入。
  二、假設當前狀態找到的最優解序列是x[0],x[1],x[2],x[3]...x[j],能使總價值最大。
     對於第j個物品是否裝入,須要進行比較
      1)當w[j]>load,當這個物品的重量大於包剩餘承重時,則沒法裝入,此時最大總價值等於前i-1個的最大總價值
               則  f(i,j)=f(i-1,j)
      2)當w[j]<=load時,
          則須要比較兩個值,f(i,j)=max{f(i-1,j-w[i])+v[i] , f(i-1,j)}
          假如要裝入第i個物品的話,則須要花掉w[i]的承重,則對於前i-1個物品來講,總價值是f(i-1,j-w[i])+v[i]
          假如不裝入的話,總價值是f(i-1,j)
          取二者的較大值。
   上面的每一個式子都包含了子序列,能夠採用動態規劃的方法解。
 
   ******下面是另一種順序。
         
         
 
 
    *******
    三、採用遞歸和非遞歸方式解答。
 
程序(非遞歸):

有編號分別爲a,b,c,d,e的五件物品,它們的重量分別是6,4,3,5,7,它們的價值分別是3,7,6,6,5,如今給你個承重爲10的揹包,如何讓揹包裏裝入的物品具備最大的價值總和?htm

採用填表法blog

名稱 weight value 0 1 2 3

4遞歸

5 6 7 8 9 10
a 6 3  0  0  0  0  0  0  3  3  3  3  3
b 4 7  0  0  0  0  7  7  7  7  7  7  10
c 3 6  0  0  0  6  7  7  7  13  13 13  13
d 5 6  0  0  0  6  7  7  7  13  13  13  13
e 7 5  0  0  0  6  7  7  7  13  13  13  13

 

只要你能經過找規律手工填寫出上面這張表就算理解了01揹包的動態規劃算法。索引

首先要明確這張表是從上往下,從左到右生成的。(****這裏和參考的那篇文章正好相反)

 這張表的填寫的規律:

  一、先填寫第0行的數據   if(load>w[i])  f(0,j)=v[i]    else  f(0,j)=0;

  二、從上往下依次填寫,f(i,j)=max{f(i-1,j-w[i])+v[i]  ,  f(i-1,j)}

     好比c10  (第c行第10列)  f(2,10)=  max {f(1,10)    ,   f(1,10-6)+6}  ->  max{10  ,  7+6}  =  13 。

  三、最大總價值天然就是   f(n-1,c)。

 

    public static void main(String[] args) {
        int[] weights={6,4,3,5,7};
        int[] values={3,7,6,6,5};
        boolean [] selected=new boolean[weights.length];
        System.out.println(getMaxReverse(weights, values, weights.length, 10));
        System.out.println(getMax(selected, weights, values, 1, 10));
        System.out.println(getMax(weights, values, 10));
    }
    /**
     * 揹包問題第一步:得到動態公式
     * f(i,j)=Max{f(i-1,j-Mi)+Pi,f(i-1,j)}
     * f(i,j)表示前i件物品放在承重j的揹包中的最大價值
     * Mi是第i件物品的重量,Pi是第i件物品的價值
     */
    public static int getMaxReverse(int [] weights,int[] values,int index,int load)
    {
        if(index==0 || load==0) return 0;
        if(weights[index-1]>load)//不選擇當前
        {
            return getMaxReverse(weights, values, index-1, load);
        }
        else
        {
            //選擇當前
            int m1=getMaxReverse(weights, values, index-1, load-weights[index-1])+values[index-1];
            //不選擇當前
            int m2=getMaxReverse(weights, values, index-1, load);
            return Math.max(m1, m2);
        }
    }
    
  //在遞歸裏面肯定裝了哪些物品不靠譜
public static int getMax(boolean[] selected,int [] weights,int[] values,int index,int load) { if(index==weights.length) return weights[index-1]>load?0:values[index-1]; if(weights[index-1]>load) { selected[index-1]=false; return getMax(selected, weights, values, index+1, load); } else { int v1=getMax(selected, weights, values, index+1, load); int v2=getMax(selected, weights, values, index+1, load-weights[index-1])+values[index-1]; if(v1>v2) { selected[index-1]=false; return v1; } else { selected[index-1]=true; return v2; } } } /**
    非遞歸解法
  */
public static int getMax(int [] weights,int[] values,int load) { //新建數組用於存放數據 //load+1的做用是取索引方便 int len=weights.length; int[][] maxSum=new int[len][load+1]; //第一步初始化第0行 for(int i=0;i<load+1;i++) { maxSum[0][i]=weights[0]>i?0:values[0]; } //從第1行到第n-1行 for(int i=1;i<len;i++) { for(int j=0;j<load+1;j++) { if(j>=weights[i]) maxSum[i][j]=Math.max(maxSum[i-1][j], maxSum[i-1][j-weights[i]]+values[i]); else maxSum[i][j]=maxSum[i-1][j]; } } for(int i=0;i<len;i++) System.out.println(Arrays.toString(maxSum[i])); //輸出選擇的物品 int temp=load; for(int i=len-1;i>0;i--) { if(maxSum[i][temp]!=maxSum[i-1][temp]) { System.out.println("取了第"+(i+1)+"件物品 重量"+weights[i]+" 價值"+values[i]); temp-=weights[i]; } } //返回 return maxSum[len-1][load]; }
相關文章
相關標籤/搜索