经过我的一通研究, 动态规划已经被我分成两大类,即分阶段决策问题以及枚举问题.在枚举界,深度优先搜索可谓明星算法,不仅如此,它还能跨界发展,因为在动态规划界,DFS天然的树状路径总让人觉得与DP的最优子结构有着暧昧不清的关系.如果我们能用DFS的思想去分析问题,然后最终打开一扇DP的大门就好了.
然而,事实证明上述想法里YY的成分多一些,可是DFS和DP确实存在着某种神秘关联,下面对01背包问题的分析就是铁证.
还没贴图,会补上的…
先把问题描述清楚:
背包最大负重为5,现有三个物品,重量是(2,3,4).相应地,三个物品的价值为(10,15,20).现在要从三个物品中选择若干件装入背包.请问:在不超出背包负重上限的前提下,怎样选取才能使包中物品总价值最大?
经观察,应选择前两个物品.
好好好满分.现在背包容量是65536,有4096个物品可以选,麻烦再来观察一下?
别跟我提贪心,我要是再相信贪心我就是DOG.
不扯淡了,其实第一个想到,也是最直观的策略,一定是枚举:
方案 | 最终重量 | 最终价值 |
---|---|---|
(0,0,0) | 0 | 0 |
(0,0,1) | 4 | 20 |
… | … | … |
(1,1,1) | 8 | 45 |
其中(0,0,0)表示”不选第一个,不选第二个,不选第三个”.
好好好,又是满分.现在背包容量是65536…
仔细分析上述枚举的过程:
算了,直接上图吧:
当处理2号物品的时候,我们需要的参数,如果背包的当前重量,已拥有的总价值等等,通通需要从树根开始计算.这肯定是不对的,正确的遍历方法是随身携带参数:
其中,节点(0,1,5)所表述的问题是
背包最大负重为5,可选物品为(0,1,2).假设我一定会选择0号物品.请问在满足题设的前提下,怎样选取剩余物品才能使背包中的价值最大?
整个问题的最终答案取决于森林中每个树的根.最终取决于森林中所有的叶.
能用动态规划解决问题吗?回想一下,动态规划的大门前永远都站着个巨大的门神:重叠子问题,最优子结构.
最优子结构已经清楚地写到递归树的脸上去了.
然而重叠子问题在哪儿?上述递归树有相同的节点吗?
找不到重叠子问题?那就往下挖.
回到递归树(森林)上.现在将注意力集中到最后一层.最后一层有两类节点,一类节点是(2,0,X),另一类是(2,1,X).各占一半.我们以(2,0,X)为例.(为了方便叙述,”第2层的节点”代表的是”森林中所有高度为2的节点”).
因为X是背包的上限,所以X非负数.又从上述分析知道,随着高度增加,X可能减少但绝不增加.再考虑到X的初始值为5.所以X最多有6个不同取值.
所以,最后一排最多有12不同的节点.
回头再看,上述分析过程中并没有用到”最后一层”这个条件,我们可以把分析应用到”第一层”或者”第二层”,这只会改变一些无关紧要的文字描述,分析过程完全不变,因此结论也完全不变.所以我们可以把结论中的”最后一层”去掉,这样结论表述为:
任意一层最多有12个不同节点.
乍一看是废话,因为森林只有3层,每层分别只有2,4,8个节点,肯定小于12.其实恰恰相反,这是个很重要的结论.
我们只要增加一个物品,比如(编号:3,重量:5,价值30),上述递归树的高度就会变成4.如果森林有第4层,它将有16个节点,由刚才的分析可以知道,这16个节点里面最多有12个不同的节点.又因为每个节点对应一个子问题,因此第4层里出现的16个子问题中,最多只有12个互不相同的子问题,即至少有4个子问题是重复的.
同理,如果森林有5层,那么第5层32个子问题里至少有20个子问题是重复的.
如果森林有6层…
重叠子问题:KAO,这都被找到了,真是R了G了.
于是我们可以愉快地使用动态规划了.
等等,作为一个严谨的学者(别笑),我们先来分析一下复杂度吧.
现在一般化我们的问题,假设我们有N个物品,背包负重上限是M,那么背包问题复杂度的两个上限分析如下.
算法的第一个复杂度上限是O(2^N).
这个上限来自于深度优先搜索.并且这个上限并没有什么存在感,也就是说这个结论告诉我们无论算法多慢你都不比惊讶.
我们来分析算法的另一个上限.
首先每个物品在递归树上占一层.
其次,考虑到重叠子问题,以及上述一通乱七八糟的分析,我们知道每层最多有(M+1)个不同子问题.
于是我们可以轻易得出另一个上限:
O(N*M)是动态规划解决背包问题的第二个复杂度上限
这样一来,最终结论就是:
动态规划解决01背包问题的复杂度是O(2^N)和O(N*M)中较小的那个.其中N是物品个数,M是背包负重上限.
什么?你说背包上限是65536,物品有4096个?那也才不到3亿次计算而已.