本人弱鸡,开始学dp时,对背包问题有许多疑惑,听了讲座却还不是很懂,看了许多博客的讲解也觉得理解的不是很透彻,就背了代码直接过了。现在查阅了一些资料,自己想了想,终于开始明悟,现在把我想到的记录下来,希望能为有相同境遇的萌新提供一些思路。
我们先来了解01背包的性质,01是指物品在背包中的状态,也就是说,现在有一个容积为 V 的背包,有 n 件物品,每件物品只有一件,他们的体积为 v[i] ,价值为 w[i], 0 就表示 背包里不装这件物品,1反之。因为他们的数量唯一,所以 只需要讨论在或不在,不需要讨论数量。
求背包里物品最大价值的滚动优化过的实现代码如下
for(int i=1;i<=n;i++)
for(int j =v ;j>=v[i];j--)
f[j] = max(f[j] , f[j - v[i]]+w[i]);
看到这里,我们不禁产生了疑问:为什么体积遍历要逆序进行?
不妨手写一组数据
背包容积 8
物品序号 1
体积 4
价值 3
现在我们用正序遍历一遍
当i = 1时
f[0] = 0 ,f[1] = 0,f[2] = 0,f[3] =0 (显然当前容积小于该物品的体积,自然就放不进去,统统初始化为0)
f[4] = max( f[4],f[0]+3) 即f[4] = max(0,0+3) = 3;
f[5] = max( f[5],f[1]+3) 即f[5] = max(0,0+3) = 3;
f[6] = max( f[6],f[2]+3) 即f[6] = max(0,0+3) = 3;
f[7] = max( f[7],f[3]+3) 即f[7] = max(0,0+3) = 3;
f[8] = max( f[8],f[4]+3) 即f[8] = max(0,3+3) = 6;
大家快看!!!!!!!!!!!!
有奇怪的事发生了,人群之中竟然钻出了两个.....两个f[4]!!!
那么问题来了,还记得01背包的性质么?这个序号为1的物品竟然用了两次!!!
相信已经有许多人发现了问题的真相了,不过不懂也不要紧,让我们来看看逆序遍历后的结果。
f[8] = max(f[8],f[4]+3)即f[8] = max(0,0+3) = 3;
f[7] = max(f[7],f[3]+3)即f[7] = max(0,0+3) = 3;
f[6] = max(f[6],f[2]+3)即f[8] = max(0,0+3) = 3;
f[5] = max(f[5],f[1]+3)即f[5] = max(0,0+3) = 3;
f[4] = max(f[4],f[0]+3)即f[4] = max(0,0+3) = 3;
f[3] = 0,f[2] = 0,f[1] = 0,f[0] = 0
不知道大家发现没有,逆序遍历在向前滚动的时候并没有对更小体积的清况进行更新,而正序时却对同一件物品进行了多次操作。
大家请看更新过程
f[4] -> 3
f[4] -> f[8]
我们先更新了f[4](0 -> 3)
却又用了已经被更新过的f[4] 来更新 没有更新过的f[8]
就相当于容积为4时已经有了物品1,而在容积为8时却还要再装一件物品1,相信如果容积更大,f[4[也会不断去更新别人,这显然是违背题意的。
基于此我们理解完全背包也就更容易了(反正有无限个,正着跑就是了,爱装几个装几个qwq)