經常使用算法思想之動態規劃的多條件記憶思路

思路:要解決的子問題不單單是數量的變化,判斷的條件也會變化,選擇同時記住子問題和變化的條件,存下全部變化條件下子問題的最優結果,做爲父問題的解答bash

揹包問題

總共n個物件,每一個物件的重量爲S_i,是個Integer,每一個物件價值爲V_i,揹包能裝下的重量爲S,求每次獲取最大價值的裝法。
分析以下:揹包能裝下的重量是有限的,若是物件自己的重量是大於揹包的能裝範圍,價值再大也不能裝,對於剩餘重量小於揹包能裝範圍:spa

  • 假設選擇裝物件A,此時獲取價值爲V_a,剩餘能裝重量 $S-S_a
  • 假設選擇不裝物件A,此時能夠在剩餘的物件裏面選擇一個能裝的

選擇的方案總共有兩種,那種方式使得價值最大就選擇對應的裝的方式.net

DP(i,X)=max(DP(i-1,X),DP(i-1,X-S_i)+V_i \space if\space S_i\leq S)
  • X表示當前揹包能裝的量
  • i-1表示揹包當前已經裝的物件
  • S_i表示當前的物件重量
  • DP(i-1,X)表示不裝物件
  • DP(i-1,X-S_i)+V_i則表示當前選擇裝能帶來的價值
  • DP(i-1,X-S_i)則表示在剩餘重量下的最大能裝的價值

假設有以下示例:code

總共有3個物件,揹包限量爲5kg。物件分別爲 
A價值10元,4kg
B價值4元,2kg
C價值7元,3kg
複製代碼

揹包裏頭剛開始什麼都沒有,也就是剛開始的時候什麼都不拿,揹包中的價值都是0。get

0 1 2 3 4 5
0 0 0 0 0 0 0
A
B
C

橫軸表示揹包能裝的重量,縱軸表示物件,每一個單元格表示對應重量中揹包能裝的最大價值string

假設這個時候只有一個物件A,它的重量是4kg,根據揹包能裝的條件必須是揹包的容量至少是4kg,不然不管價值是多少,容量不夠確定不能裝it

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0
B
C

列爲0表示容量爲0io

此時容量已經到達4了,有兩個選擇table

  • 不裝A,此時帶來價值是0,剩餘容量是5
  • 裝A,帶來的價值是10,損失的容量是4,剩餘容量是0,在原來容量是1且沒有物件可裝的狀況下,DP(0,0)它的價值是0

能夠看到裝A帶來的價值更大class

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10
B
C

當容量到達5時,和4作同樣的考慮,有DP(0,1)+10=0+10>DP(0,5)=0,於是繼續選擇裝A價值更大

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B
C

物件A的選擇已經窮盡,此時能夠看作'A已經裝在揹包了'。考慮物件B,它的重量爲2kg

  • 只有在揹包有容量裝時,才能帶來價值
  • 當容量爲2時,A是裝不下的,此時原始揹包的價值爲0,裝能帶來價值
  • 當容量爲3是,仍是隻能裝下B
0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4
C

當容量爲4的時候狀況出現了一點變化

  • 不裝B,此時揹包的已經裝下了A,並且價值DP(1,4)=10
  • 裝B,帶來的價值爲4,剩餘容量爲3,而在容量爲3時,初始的價值爲DP(1,3)=0,總價值爲4

能夠看到A,B二者在容量爲4時,選擇只裝A是更優的選擇,一樣的狀況適用於容量5

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4 10 10
C

而後考慮C,此時揹包已經考慮過裝A,B的狀況

  • 當容量爲0和1的時候,原始揹包什麼都沒有裝,並且容量也達不到C的重量
  • 當容量爲2的時候,仍然裝不下C,而不裝C,從A,B中的結果來看,能夠獲得的最佳價值爲DP(2,2)=4
0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4 10 10
C 0 0 4

當容量爲3時

  • 不裝C,原始價值爲DP(2,3)=4
  • 裝C,原始價值爲 7+DP(2,0)=7

DP(2,0)表示在容量爲0的狀況下,A+B都考慮是否裝的最優價值

得出只裝C更好

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4 10 10
C 0 0 4 7

當容量爲4時

  • 不裝C,原始價值爲DP(2,4)=10
  • 裝C,帶來價值7,損失重量3,剩餘1,總價值爲DP(2,1)+7=7

不裝C更好

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4 10 10
C 0 0 4 7 10

當容量爲5時

  • 不裝C,價值爲DP(2,5)=10
  • 裝C,帶來價值7,損失重量3,剩餘重量2,總價值DP(2,2)+7=4+7=11

裝C更好

0 1 2 3 4 5
0 0 0 0 0 0 0
A 0 0 0 0 10 10
B 0 0 4 4 10 10
C 0 0 4 7 10 11

由此能夠獲得結論,容量爲5,當前條件下最優的價值是11。
代碼以下

public static void main (String[] args) throws Exception{
//code
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
int round=Integer.parseInt(br.readLine());
for(int r=1;r<=round;r++){
            //物件的個數
            int N=Integer.parseInt(br.readLine());
            //揹包的容量
            int W=Integer.parseInt(br.readLine());
            String[] vStrs=br.readLine().split(" ");
            String[] wtStrs=br.readLine().split(" ");
            int[] v=new int[N+1];
            int[] wt=new int[N+1];
            for(int i=0;i<N;i++){
                //物件的價值
                v[i]=Integer.parseInt(vStrs[i]);
                //物件的重量
                wt[i]=Integer.parseInt(wtStrs[i]);
            }
            int[][] dpV=new int[N+1][W+1];
            for(int i=1;i<=N;i++){
                for(int j=1;j<=W;j++){
                    int result=dpV[i-1][j];
                    if(wt[i-1]<=j){
                        result=Math.max(v[i-1]+dpV[i-1][j-wt[i-1]],dpV[i-1][j]);
                    }
                    dpV[i][j]=result;
                }
            }
            System.out.println(dpV[N][W]);
}
}
複製代碼

總共的耗時時間爲O(NW),與揹包的容量和物件的個數都有關。

僞多項式運行時間

從上面看到揹包問題須要的時間爲O(ns),其中s表示揹包的最大容量,一個物件的最大容許放的大小就是S,存放在電腦上起碼須要 logS 的空間。而總共的個數有 n 個須要處理,因此花銷的空間大小爲 O(nlogS),假設logS=b,即輸入大小爲 O(nb),那麼運行時間爲O(n.2^b),也就是隨着輸入的變大,運行時間會變成指數增加,可是若是b很小,就是多項式運行時間,這種稱爲僞多項式運行時間。

相關文章
相關標籤/搜索