算法分析与设计——动态规划求解01背包问题

1、算法思想

  • 假设有四个物品,如下图,背包总容量为8,求背包装入哪些物品时累计的价值最多。
  • 我们使用动态规划来解决这个问题,首先使用一个表格来模拟整个算法的过程。
    • 表格中的信息表示指定情况下能产生的最大价值。例如,(4, 8)表示在背包容量为8的情况下,前四个物品的最佳组合所能累计的最大价值。
    • 【注】第一行全0,因为第一行考虑的是前0个物品的最佳组合,也就是没有物品,它存在的意义是方便后续计算;第一列全0,因为第一列背包容量为0,不能放入任何物品,所以价值为0。
  • 现在我们需要一步一步将这个表格填好。
  • 考虑(1, 1)
    • 表示前1个物品在背包容量为1的情况下,能装入背包的最佳组合所能累计的最大价值为多少。
    • 已知,1号物品体积为2,价值为3。不能装入背包中,所以此时能产生的最大价值应该和前0个物品的一致,也就是0。
  • 考虑(1, 2)
    • 此时背包容量为2,前1个物品也就是1号物品刚好可以装入。那么需要考虑要不要将该物品装入背包。
    • 如果不装,那么此时的价值应该和前0个物品的一致,也就是0。
    • 如果装,那么此时的价值应该是该商品的价值+剩余容量所能装入的最大价值(前0个商品,2-2=0个容量),也就是3+0=3。
    • 然后把第二行填完,即后面都为3,因为只有第一个商品。
    • 2号商品体积为3,价值为4。第三行的前三列(前2个物品,容量2以内)因为不能放入2号商品,所以最大价值与前1个物品一致。
    • 考虑(2, 3)
      • 背包容量为3,可以放下2号物品。那么考虑要不要将该物品装入背包。
      • 如果不装,那么此时的价值应该和前1个物品的一致,为3。
      • 如果装,那么此时的价值应该是该商品的价值+剩余容量所能装入的最大价值(前1个商品,3-3=0个容量),也就是4+0=4。
    • 后面的就不再赘述,最终得到下面的表。而(4, 8)就是所能累计的最大价值
  • 归纳
    • 如果装不下当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
    • 如果装得下当前物品(选取假设1和假设2中较大的价值,为当前最佳组合的价值)
      • 假设1:装当前物品,在给当前物品预留了相应空间的情况下,前n-1个物品的最佳组合加上当前物品的价值就是总价值。
      • 假设2:不装当前物品,那么前n个物品的最佳组合和前n-1个物品的最佳组合是一样的。
  • 虽然得到了在指定条件下,背包能装入的最大价值,但是还不知道背包中到底装了哪些物品
  • 接下来就需要进行回溯,查看每种情况下背包所装入的物品。
  • 从后往前看,(4, 8)的价值为10,考虑有没有把4号商品装入背包中。
    • 如果4号商品没有装入背包中,那么此时的价值应该和前3个商品的一致,即为9,所以4号商品是装入了背包的。
  • 减去4号商品的体积,剩下3个容量。接下来就只需要看前3个商品,容量为3的情况,即(3, 3)。
    • (3, 3)的体积为4,考虑有没有把3号商品装入背包中。因为此时的价值与前2个商品的一致,所以3号商品没有装入背包中。
  • 剩余体积不变,继续考虑前2个商品,也就是(3, 2)。
    • (3, 2)的体积为3,考虑有没有把2号商品装入背包中。因为此时的价值与前1个商品的不一致,所以2号商品是装入了背包的。
  • 减去2号商品的体积,剩下0个容量。没办法装入其他的商品了,到此结束。
  • 所以,当2号商品和4号商品装入背包时,能产生最大价值。
  • 归纳
    • 从表的右下角开始回溯,如果发现前n个物品最佳组合的价值和前n-1个物品最佳组合的价值一样,说明第n个物品没有被装入;否则,第n个物品被装入。

2、实例

  • 采用动态规划算法求解01背包问题,背包体积为100,商品体积和价格如下表:
  • python代码
    • # 求最大价值
      def max_values(goods, bag_size, goods_num):
          # 创建一个二维数组来存放最大价值,100个体积,16个商品,17行,101列(保留一列为全0)
          values = [[i for i in range(bag_size + 1)] for i in range(goods_num + 1)]
          # 计算每种情况下的最大价值
          for i in range(1, goods_num + 1):
              for j in range(1, bag_size + 1):
                  if goods[i][0] <= j:  # 商品体积小于等于背包容量
                      # 比较放入该商品和不放入该商品产生的最大价值
                      values[i][j] = max(values[i - 1][j], goods[i][1] + values[i - 1][j - goods[i][0]])
                  else:  # 商品不能放入背包,此时最大价值等于前i-1个商品的最大价值
                      values[i][j] = values[i - 1][j]
          return values
      
      
      # 回溯,求哪些商品装入了背包
      def search_goods(goods, values, bag_size, goods_num):
          good = []
          i = goods_num
          j = bag_size
          while values[i][j] != 0:
              if values[i][j] != values[i - 1][j]:  # 如果发现前n个物品最佳组合的价值和前n-1个物品最佳组合的价值不一样,说明第n个物品被装入了背包
                  good.append(i)
                  j -= goods[i][1]  # 减去该商品的体积
                  i -= 1
              else:  # 第n个物品没有被装入
                  i -= 1
          return good
      
      
      if __name__ == '__main__':
          # 共有16个商品,商品的体积和价格
          goods = [[0, 0], [5, 6], [8, 3], [28, 19], [9, 20], [6, 15], [18, 12], [21, 10], [19, 22], [19, 8], [23, 18],
                   [10, 14], [9, 13], [34, 18], [26, 19], [38, 33], [9, 10]]
          bag_size = 100  # 背包体积
          goods_num = 16  # 商品数量
          # price = [5, 8, 28, 9, 6, 18, 21, 19, 19, 23, 10, 9, 34, 26, 38, 9]
          # volume = [6, 3, 19, 20, 15, 12, 10, 22, 8, 18, 14, 13, 18, 19, 33, 10]
          values = max_values(goods, bag_size, goods_num)
          for i in range(goods_num + 1):  # 打印列表
              print(values[i])
          good = search_goods(goods, values, bag_size, goods_num)
          print("产生的最大价值为:", values[goods_num][bag_size])
          print("放入背包的商品为:", good)
  • 运行结果

猜你喜欢

转载自blog.csdn.net/weixin_45100742/article/details/134561642