0-1背包问题-例题

138 阅读3分钟

假设:背包最大重量为4,我们有三个物品。重量和价值如下表所示:

weightvalue
item0115
item1320
item2430

明确dp数组的含义

即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

dp[i][j]表示:从下标为[0-i]的物品里随意取,放进容量为j的背包里,价值总和最大是多少?

用图示的方式画出来,j=0也就是红框中背包容量=0的时候,这个时候肯定啥也装不下,所以格子里全都是0. image.png 此时来看i=0的情况,也就是物品0。重量1~4的背包都能放下它,所以这里可以更新价值为物品0的价值15. image.png 接下来,看物品1的情况。也就是dp[1][j]这一行的更新。 很明显,物品1的重量是3,背包1放不下,所以背包1还是保留它之间的状态,也就是只装物品0。 这句话我们用公式表示就是dp[i][j]=dp[i-1][j],意思就是这个重量j背包的上一个状态转移到现在的背包。 image.png 对于重量2的背包也是一样!因为它也装不下物品1. image.png 现在来到重量3的背包,它装得下物品1,但是它只能装下物品1,这个时候就要取舍了,是装物品1还是装其他的物品价值大呢?装物品1的价值是20,装物品0的价值是15,那肯定选择装物品1.

image.png

这个选择的过程的体现是直接:dp[i][j]=Math.max(dp[i-1][j],value[i])就好了吗?

当然不是! 在这里我们很直观的直接选择了物品1的价值,因为背包刚好能装下。

我们看下一种情况:

当背包重量4的时候,完全可以放下重量等于3的物品1,甚至还有一个重量单位的剩余空间! 那么怎么办呢?既然有剩余,我们就去找这个重量1的物品就好了,但是我们换一种方式,我们不去物品列表里搜索重量1的,我们去dp表里搜索重量1的背包的价值就好了,因为重量1的背包肯定只能装重量1的物品,所以我们去找重量1背包的状态就好了。

这才是最后的公式

dp[i-1][j-weight[i]]=dp[0][4-3]=dp[0][1]=15.
dp[i-1][j]=dp[i-1][j-weight[i]]+value[i]

image.png

在看完上述的过程,梳理出递归公式了吗? 分为多种情况吧

  • 背包放得下吗?
    • 放不下:dp[i][j]=[dp[i-1][j]
    • 放得下:拿还是不拿?
      • 拿:dp[i][j]=dp[i-1][j-weight[i]]+value[i]
      • 不拿:dp[i][j]=dp[i-1][j]

image.png

实际上放不下的情况和放得下但是不拿的情况,公式是一样的,我们可以总结成一个公式:

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

总结一下:

最难的一点就是关于拿还是不拿的决策,要理解这个公式dp[i-1][j-weight[i]]还是挺难的。 我们把j想象成当前背包的空间,而weight[i]是物品i的重量,当物品放进背包里的时候占了空间,那就挤占了别的物品的空间,背包还可以装下物品的空间就被压缩了,剩下j-weight[i]的空间给别的物品了。