• 【转】经典算法:背包问题


    本文由 ImportNew - hejiani 翻译自 javacodegeeks。欢迎加入Java小组。转载请参见文章末尾的要求。

    背包问题很有意思,同时也富有挑战性。首先看一下这个问题的完整描述:

    问题

    假定背包的最大容量为W,N件物品,每件物品都有自己的价值和重量,将物品放入背包中使得背包内物品的总价值最大。

    背包问题wiki

    可以想象这样一个场景——小偷在屋子里偷东西,他带着一只背包。屋子里物品数量有限——每件物品都具有一定的重量和价值——珠宝重量轻但价值高,桌子重但价值低。最重要的是小偷背包容量有限。很明显,他不能把桌子分成两份或者带走珠宝的3/4。对于一件物品他只能选择带走或者不带走。

    示例:

    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。

    解决方法:

    最佳的解决方法是使用动态规划——先得到该问题的局部解然后扩展到全局问题解。

    构建物品X在不同重量时的价值数组V(Value数组):

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

    该矩阵中的每个值的求解都代表一个更小的背包问题。

    初始情况一:对于第0列,它的含义是背包的容量为0。此时物品的价值呢?没有。因此,第一列都填入0。

    初始情况二:对于第0行,它的含义是屋内没有物品。那么没有任何物品的背包里的价值多少呢?还是没有!所有都是0。

    步骤:

    1、现在,开始填入数组每一行的值。第1行第1列代表什么含义呢?对于第一个物品,可以把重量为1的该物品放入背包吗?不行。第一个物品的重量是5。因此,填入0。实际上直到第5列(重量5)之前都应该填入0。
    2、对于第1行的第5列(重量5),意味着将物品1放入背包。填入10(注意,这是Value数组):

    3、继续,对于第6列,我们可以再放入重量为1(重量值-物品的重量)的物品吗。我们现在只考虑物品1。由于我们加入物品1之后就不能再加入额外的重量,可以很直观地看到其余的列都应该还是相同的值。

    4、接着,有意思的事情就要出现了。在第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

    博客地址: http://www.cnblogs.com/dwf07223,本文以学习、研究和分享为主,欢迎转载,转载请务必保留此出处。若本博文中有不妥或者错误处请不吝赐教。

  • 相关阅读:
    HTML知识点总结之table
    HTML知识点总结之表单元素
    HTML知识点总结之div、section标签
    使用js脚本批量下载慕课网视频
    一种table超出高度自动出滚动条的解决方案
    C#算法之判断一个字符串是否是对称字符串
    强强联合之jquery操作angularjs对象
    锋利的js之验证身份证号
    锋利的js前端分页之jQuery
    超高性能的json序列化之MVC中使用Json.Net
  • 原文地址:https://www.cnblogs.com/dwf07223/p/3983896.html
Copyright © 2020-2023  润新知