背包详解:01 背包

目录

  • 简单 01 背包
    • 空间复杂度优化
    • 另一种状态转移
  • 普通 01 背包
  • 求方案数
  • 刚好填满背包
  • 总结

  刷掉了《剑指offer》的天梯后,感觉自己对动态规划,回溯,枚举等类型的问题还感觉十分生疏,就打算把这些类型挑出来个个击破。

  背包问题是动态规划的入门问题之一,于是我找到了师兄之前推荐给我的《背包九讲》,就着 Lintcode 的 backpack 天梯,学习了一下这个方面的问题。

简单 01 背包

有一个大小为 m 的背包,有 N 个物品, 每个物品的重量为 A_i,最多能装多满?(Lintcode-92)

  这就是最简单的 01 背包问题,01 指得是给的物品只有两种状态,放进包里,或者没放进包里。

  如果不知道动态规划,一般脑子里会浮现出贪心的思想,先放最大,然后慢慢填满。如果有测试集,很快就会发现这个思想并不是每步最优,最终导致全局也不是最优解。

  如果贪心不能用,那么就枚举吧,一个 N 个物品,每次放一个,把所有情况都存下来,但感觉存储每步的结果好像很麻烦,但是没关系,我们先用一个测试集感受一下使用枚举寻找最优解的过程,在这个过程中来寻找存储的方法。

  假如背包大小为11,有 4 个物品,大小分别为 [2, 3, 5, 7]。

  • 最初始,背包里没有物品,记为 {0, {}},表示背包重量为 0,后面花括号中为已经装入的物品列表
  • 第一次,放入 2,记为 {2, {2}}
  • 第三次,放入 3,从背包为 0 时放入,得到 {3, {3}},从背包重量为 2 时放入,得到 {5, {2, 3}}
  • 第三次,放入 5,从背包为 0 时放入,可以得到 {5, {5}}, 我们已经有 {5, {2, 3}},但这个问题中我们不记录装的内容,随便保留一个就好了,选择保留新的 {5, {5}}。从背包为 2,3,5 时放入,分别得到 {7, {2, 5}}, {8, {3, 5}}, {10, {2, 3, 5}}
  • 第四次,放入 7,可以得到 {7, 0}, {9, {2, 7}}, {10, {3, 7}}, {12, {5, 7}}, 不对,背包大小为 11,这个结果不用记,背包重量为 7,8 和 10 时更加装不下,都不用记录

列一下每一步的表:
1. {0, {0}}
2. {0, {0}}, {2, {2}}
3. {0, {0}}, {2, {2}}, {3, {3}}, {5, {2, 3}}
4. {0, {0}}, {2, {2}}, {3, {3}}, {5, {5}}, {7, {2, 5}}, {8, {5, 3}}, {10, {2, 3, 5}}
5. {0, {0}}, {2, {2}}, {3, {3}}, {5, {5}}, {7, {7}}, {8, {5, 3}}, {9, {2, 7}}, {10, {3, 7}}

  我们可以得出最多装入的重量为 10, 那么问题来了,会出现枚举重复吗?这个枚举覆盖到所有情况了吗?我们需要多大的存储空间来完成枚举?

  我们每一轮放入的都是不同的物体,不会重复。每一轮都尝试过把目前的物体放入所有上一轮的结果,从列表的过程中也可以看出,肯定覆盖全面了。存储空间的话,只要记录下上面的表就可以了,由于背包内部不用记录,超出背包容量的情况不需要记录,所以只要用 物品个数 * 背包容量 大小的二维数组来存储就好了。

我们来画出这个二维数组:

轮数 i 物品重量 A_i 背包重量 j 1 2 3 4 5 6 7 8 9 10 11
0 - 0 0 0 0 0 0 0 0 0 0 0 0
1 2 0 0 2 0 0 0 0 0 0 0 0 0
2 3 0 0 2 3 0 5 0 0 0 0 0 0
3 5 0 0 2 3 0 5 0

你可能感兴趣的:(算法,算法与数据结构,背包,01背包)