目录
刷掉了《剑指offer》的天梯后,感觉自己对动态规划,回溯,枚举等类型的问题还感觉十分生疏,就打算把这些类型挑出来个个击破。
背包问题是动态规划的入门问题之一,于是我找到了师兄之前推荐给我的《背包九讲》,就着 Lintcode 的 backpack 天梯,学习了一下这个方面的问题。
有一个大小为 m 的背包,有 N 个物品, 每个物品的重量为 A_i,最多能装多满?(Lintcode-92)
这就是最简单的 01 背包问题,01 指得是给的物品只有两种状态,放进包里,或者没放进包里。
如果不知道动态规划,一般脑子里会浮现出贪心的思想,先放最大,然后慢慢填满。如果有测试集,很快就会发现这个思想并不是每步最优,最终导致全局也不是最优解。
如果贪心不能用,那么就枚举吧,一个 N 个物品,每次放一个,把所有情况都存下来,但感觉存储每步的结果好像很麻烦,但是没关系,我们先用一个测试集感受一下使用枚举寻找最优解的过程,在这个过程中来寻找存储的方法。
假如背包大小为11,有 4 个物品,大小分别为 [2, 3, 5, 7]。
列一下每一步的表:
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 |