背包问题,简单明了,快速回忆

问题描述

有N种物品和一个容积为V的背包,每种物品i都有(1,无限,有限k[i])个,都有体积cost[i]和价值value[i],问如何选取物品使得放入背包的物品价值之和最大。

解决方法

  • 画表,横坐标c表示剩余容积,纵坐标i表示物品编号
  • 填表,表中的值B(i,c)表示的是在剩余空间为c的情况下,取前i个物品能拿到的最大价值
  • 取出B(N,V)即在空间为V的背包中,在前N件物品中挑选所能拿的最大价值,也就是问题的答案

例子

假设有物品A的价值5,代价5。物品B的价值7,代价8,总容量为10,求最大价值

cost value i\c 0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0 0 0 0
5 5 1 0 0 0 0 0 5 5 5 5 5 5/10
8 7 2 0 0 0 0 0 5 5 5 7 7 7/10
  • B(1,5) = 5表示在剩余空间为5,只有编号1物品的情况下能拿的最大价值为5;
  • B(2,9) = 7 表示在剩余空间为9,编号可选1,2的情况下能拿的最大价值为7;
  • 编号是和容量都是从1开始的,0表示没得选或没容量;
  • B(1,10)和B(2,10)表示不同规则(同一件物品能拿1次/无限次/有限次),也对应三种背包问题;

如何填表

方法一:直接画二维表

  • 创建一个二维数组N+1行V+1列,全初始化为0;
  • 两个for循环填过去,其中对于一般的值B[i][c]有三种情况
    • 容量不够,即c < cost[i]
      • B[i][c] = B[i-1][c]
    • 容量够,取最大值
      • 不取
        B[i][c] = B[i-1][c]

      • B[i][c] = B[i-1][c-k·cost[i]]+k·value[i]
  1. 装不下去和不取结果都是取上一行即不存在i的情况下的值B[i-1][c]
  2. 把剩余空间看成所需空间,你要装i,那么你所需的空间就要加上k倍的cost[i],并且值加上value[i]。
  3. k意思是取几个i,其范围(1、1-V/cost[i]、1-k[i])取决于哪种背包问题,通过for循环遍历整个k并选择最大作为结果

方法二:用一维数组

二维数组是一行一行更新,如果不在意前面怎么取的话可以用一个一维数组,每次更新一维数组即一行,更新i次,更新完取最后个值即答案

  • 两次for循环,其中都是i从1到N,对于每个c,遍历所有k取下面情况的最大值
    • 容量不够或者不取
      • B[i]不动,还是上个值
      • B[i] = B[i-k·cost[i]]+k·value[i]
      • 其中当k只能为1时,c必须从后往前遍历,c取[N,1]
      • 其他情况下,c从前往后遍历,c取[1,N]
  1. 当k限制为1即0-1背包问题时,因为如果c顺序是从前往后,会出现:首先前cost[i]-1肯定不变,因为装不下,然后填充cost[i]到2倍的cost[i]-1时也没问题,可接下去填充2·cost[i]时,需要用到上一行cost[i]的值,此时已被替代成拿一个i的情况了,所以不行。
  2. 其他情况下,前面说过,当填2·cost[i]时,这里需要的就是可以拿多个的情况,必须要更新后的值,所以是从前往后。

你可能感兴趣的:(算法)