01揹包問題

1.揹包問題算法

(1)問題由來:給定n個重量爲w1,w2..........wn,價值爲v1,v2........,vn的物品和一個承重爲W的揹包,求這些物品中最有價值的一個子集,並要求可以裝到揹包中。這裏假設全部的重量和包的承重都是正整數,而物品的總重量沒必要是整數。spa

(2)地推公式:爲了設計一個動態規劃算法,須要推導一個遞推關係,用較小實例解的形式來表示揹包問題的實例的解。接下來咱們來考慮一個由前i個物品定義的實例,物品的重量分別w1,w2,w3,..wi,價值分別爲v1,v2,.........vi,揹包承受的重量爲j(j>=1,j<=w)。設F(i,j)爲該實例最優解的物品總價值,也就是說可以放進承重爲j的揹包中的前i個物品中最有價值本身的總價值。能夠將前i個物品中可以放進承重爲j的揹包中的子集分爲兩個類別:包括第i個物品的子集和不包括第i個物品的子集。設計

(3)根據上面的描述有下面的結論:3d

  根據定義,在不包括第i個物品的子集中,最優子集的價值爲F(i-1,j)。code

  在包括第i個物品的子集中,最優子集是由該物品和前i-1個物品中可以放進承重爲j-wi的揹包的最優子集組成。這種最優子集的價值爲vi+F(i-1,j-w)blog

(4)所以,在前i個物品中最優解的總價值爲兩個價值中的較大值。固然若是第i個物品不可以放進揹包,則從前i個物品中選出的最優子集的總價值等於從前i-1個物品中選出的最優子集的總價值。這個結果又下面的遞推公式。it

咱們能夠比較容易的定義以下的初始條件:當j>=0的時候,F(0,j)=0;當i>=0的時候,F(i,0)=0class

咱們的目標是F(n,w),即n個給定的物品中可以放進承受重量爲w的揹包中的子集的最大總價值以及最優子集自己。當i,j>0的時候,爲了計算第i行第j列的單元格F(i,j),咱們拿前一行同一列的單元格與vi加上前一行左邊wi列的單元格的和做比較,計算出二者的較大者。test

(5)實例:考慮下列數據給出的實例效率

下圖給出了由動態規劃公示計算的動態規劃表

最大的總價值爲F(4,5)=37能夠經過上表回溯過程來求得最優子集元素,由於F(4,5)=37,F(3,5)=32,物品4填滿揹包餘下5-2=3個單位承重量的一個最優子集都包括在最優解中。

(6)記憶化

 動態規劃所涉及問題的解知足一個交疊子問題來表示遞推關係。直接使用自頂向下的這樣一個地推關係求解致使算法要不止一次的求解公共子問題,所以算法的效率比較低(通常來講是指數級的),另外一方面經典動態規劃是自底向上工做的,它用全部較小的子問題來填充表格,可是每一個子問題只解一次,這種方法沒法使人滿意的一面是,在求解給定問題時,有些較小子問題的解經常不是必須的。因此咱們使用記憶法,該方法使用自頂向下的方式對給定問題進行求解,可是還須要維護一個相似自底向上動態規劃算法使用的表格。算法思想以下:

 

 2.實例:

 

package com.nowcoder.dp;

import org.junit.Test;

public class Knapsack {
    public static void main(String[] args){

    }

    /**
     *
     * @param val 實例最優解的最大值
     * @param wight 物品的重量
     * @param w 揹包容量
     * @return
     */

    public static int knapsack(int[] val,int[] wight,int w){
        int n = val.length;   //物品的總數量
        int[][] v= new int[n+1][w+1];   //建立揹包矩陣
        //對於第0行全部的列來講,他們沒有選擇物品的權利不能選擇物品,因此無論揹包容量多少,總價值都是0
        for(int col = 0 ; col<= w ; col++){
          v[0][col] = 0;
        }
        //對於第0列全部的行來講,揹包容量爲0,不能再向揹包中聽任何物品
        for(int row = 0 ; row <= n ;row++){
            v[row][0]=0;
        }
        /**
         * 接下來填充記錄表
         */
        for(int i = 1 ; i <=n;i++){//先一行一行的填充
            for(int j =1 ; j <= w ; j++){  //再在每一行中按列來填充
                if(wight[i-1]<=w){//若是當前物品的重量小於當前揹包的重量
                    v[i][j] = Math.max(val[i-1]+v[i-1][w-wight[i-1]],v[i-1][w]);
                }else {//若是當前物品的重量大於當前揹包中的重量
                    v[i][j] = v[i-1][j];

                }
            }
        }
        return v[n][w];
    }
    @Test
    public void test(){
        int[] val = {10, 40, 30, 50};
        int[] wight={5, 4, 6, 3};
        int w = 10 ;
        System.out.println(knapsack(val,wight,w));
    }
}
相關文章
相關標籤/搜索