【轉】經典算法:揹包問題

本文由 ImportNew - hejiani 翻譯自 javacodegeeks。歡迎加入Java小組。轉載請參見文章末尾的要求。html

揹包問題頗有意思,同時也富有挑戰性。首先看一下這個問題的完整描述:java

問題

假定揹包的最大容量爲W,N件物品,每件物品都有本身的價值和重量,將物品放入揹包中使得揹包內物品的總價值最大。算法

揹包問題wiki數組

能夠想象這樣一個場景——小偷在屋子裏偷東西,他帶着一隻揹包。屋子裏物品數量有限——每件物品都具備必定的重量和價值——珠寶重量輕但價值高,桌子重但價值低。最重要的是小偷揹包容量有限。很明顯,他不能把桌子分紅兩份或者帶走珠寶的3/4。對於一件物品他只能選擇帶走或者不帶走。less

示例:

1 Knapsack Max weight : W = 10 (units) 
2 Total items         : N = 4
3 Values of items     : v[] = {10, 40, 30, 50} 
4 Weight of items     : w[] = {5, 4, 6, 3}

從示例數據大體估算一下,最大重量爲10時揹包能容納的物品最大價值爲50+40=90,重量爲7。ide

解決方法:

最佳的解決方法是使用動態規劃——先獲得該問題的局部解而後擴展到全局問題解。spa

構建物品X在不一樣重量時的價值數組V(Value數組):翻譯

1 V[N][W] = 4 rows * 10 columns

該矩陣中的每一個值的求解都表明一個更小的揹包問題。3d

初始狀況一:對於第0列,它的含義是揹包的容量爲0。此時物品的價值呢?沒有。所以,第一列都填入0。code

初始狀況二:對於第0行,它的含義是屋內沒有物品。那麼沒有任何物品的揹包裏的價值多少呢?仍是沒有!全部都是0。

步驟:

一、如今,開始填入數組每一行的值。第1行第1列表明什麼含義呢?對於第一個物品,能夠把重量爲1的該物品放入揹包嗎?不行。第一個物品的重量是5。所以,填入0。實際上直到第5列(重量5)以前都應該填入0。
二、對於第1行的第5列(重量5),意味着將物品1放入揹包。填入10(注意,這是Value數組):

三、繼續,對於第6列,咱們能夠再放入重量爲1(重量值-物品的重量)的物品嗎。咱們如今只考慮物品1。因爲咱們加入物品1以後就不能再加入額外的重量,能夠很直觀地看到其他的列都應該仍是相同的值。

四、接着,有意思的事情就要出現了。在第3行第4列,此時重量爲4。

須要做如下判斷:

  1. 能夠放入物品2嗎——能夠。物品2的重量爲4。
  2. 不加入物品2的話當前已有物品的重量的Value值是否更大——查看相同重量時的前一行的值。不是。前一行的值爲0,重量4時不能放入物品1。
  3. 在這個重量時能夠放入兩件物品使得價值最大嗎?——不能。此時重量減去物品2的重量後爲0。

爲何是前一行?

簡單來講,重量爲4的前一行的值自己就是個更小的揹包問題解,它的含義是到該重量時揹包內物品的最大價值(經過遍歷物品獲得)。

舉個例子:

  1. 當前物品價值 = 40
  2. 當前物品重量 = 4
  3. 剩餘重量 = 4-4 = 0
  4. 查看上面的行(物品1或者其他行的值)。剩餘容量爲0時,能夠再容納物品1嗎?對於該給定的重量值上面的行還有任何值嗎?

計算過程以下:

1) 計算不放入該物品時該重量的最大價值:

1 previous row, same weight = 0
2  
3 => V[item-1][weight]

2) 計算當前物品的價值 + 能夠容納的剩餘重量的價值

1 Value of current item
2 + value in previous row with weight 4 (total weight until now (4) - weight of the current item (4))
3  
4 => val[item-1] + V[item-1][weight-wt[item-1]]

找到兩者之中的最大值40(0和40)。

3) 下一次最重要的位置爲第2行第9列。意味着此時重量爲9,放入兩件物品。根據示例數據如今能夠放入兩件物品。咱們做了如下判斷:

1 The value of the current item = 40
2 The weight of the current item = 4
3 The weight that is left over = 9 - 4 = 5
4 Check the row above.  At the remaining weight 5, are we able to accommodate Item 1.

計算以下:

1. 不加入該物品時該重量的最大價值:

1 previous row, same weight = 10

2. 計算當前物品的價值+能夠容納的剩餘重量的價值

1 Value of current item (40)
2 + value in previous row with weight 5 (total weight until now (9) - weight of the current item (4)) 
3  
4 = 10

10vs50 = 50。

解決了全部的子問題以後,返回V[N][W]的值——4件物品重量爲10時:

複雜度

解法的複雜度很是直觀。在N次循環中有W次循環 => O(NW)

實現

Java代碼實現:

 1 public class Knapsack {
 2     public static void main(String[] args) throws Exception {
 3         int val[] = {10, 40, 30, 50};
 4         int wt[] = {5, 4, 6, 3};
 5         int W = 10;
 6  
 7         System.out.println(knapsack(val, wt, W));
 8     }
 9  
10     public static int knapsack(int val[], int wt[], int W) {
11         //Get the total number of items. 
12         //Could be wt.length or val.length. Doesn't matter
13         int N = wt.length; 
14  
15         //Create a matrix. 
16         //Items are in rows and weight at in columns +1 on each side
17         int[][] V = new int[N + 1][W + 1]; 
18  
19         //What if the knapsack's capacity is 0 - Set
20         //all columns at row 0 to be 0
21         for (int col = 0; col <= W; col++) {
22             V[0][col] = 0;
23         }
24  
25         //What if there are no items at home.  
26         //Fill the first row with 0
27         for (int row = 0; row <= N; row++) {
28             V[row][0] = 0;
29         }
30  
31         for (int item=1;item<=N;item++){
32             //Let's fill the values row by row
33             for (int weight=1;weight<=W;weight++){
34                 //Is the current items weight less
35                 //than or equal to running weight
36                 if (wt[item-1]<=weight){
37                     //Given a weight, check if the value of the current 
38                     //item + value of the item that we could afford 
39                     //with the remaining weight is greater than the value
40                     //without the current item itself
41                     V[item][weight]=Math.max (val[item-1]+V[item-1][weight-wt[item-1]], V[item-1][weight]);
42                 }
43                 else {
44                     //If the current item's weight is more than the
45                     //running weight, just carry forward the value
46                     //without the current item
47                     V[item][weight]=V[item-1][weight];
48                 }
49             }
50  
51         }
52  
53         //Printing the matrix
54         for (int[] rows : V) {
55             for (int col : rows) {
56                 System.out.format("%5d", col);
57             }
58             System.out.println();
59         }
60  
61         return V[N][W];
62     }
63 }

 

運行結果:

    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0   10   10   10   10   10   10
    0    0    0    0   40   40   40   40   40   50   50
    0    0    0    0   40   40   40   40   40   50   70
    0    0    0   50   50   50   50   90   90   90   90
90

 


 

原文連接: javacodegeeks 翻譯: ImportNew.com hejiani
譯文連接: http://www.importnew.com/13072.html
轉載請保留原文出處、譯者和譯文連接。]


 

本文來源:http://www.importnew.com/13072.html

相關文章
相關標籤/搜索