dp[i][v]=max{ dp[i-1][v] , dp[i-1][ v-c[i] ] + w[i] }。好了,现在思考如何将数组压缩,对于这两种情况下dp[i][v]值的改变,要么是dp[i][v]=dp[i-1][v],要么是dp[i][v]=dp[i-1][ v-c[i] ] + w[i]。假设下面是就是二维数组dp的一部分,
a b dp[i-1][v] d e
f g dp[i][v] h k
我们可以发现:如果dp[i][v]=dp[i-1][v],那么相当于直接复制dp[i][v]上面的元素dp[i-1][v]值。而如果dp[i][v]=dp[i-1][ v-c[i] ] + w[i],注意到,v-c[i]<=v,所以,dp[i][v]的值是由上面红色的元素加上w[i]得到,也就是说,我们每次想要更新dp[i][v],可能会用到的值只有上面红色的部分,所以,我们就能把二维数组压缩为一维数组,只需要每次从后往前更新dp[i][v]的值。这样就用dp[v]来表示容量为v的情况下,背包内物品的价值,状态转移方程也就成了:
dp[v]=max{ dp[v] , dp[ v-c[i] ] + w[i] }
对于完全背包,一件物品可以取多次,我们仍然使用0/1背包的思想:这件物品,取还是不取。唯一的变化是,取了这件物品,还可以取。所以,如果取,仍然是i件物品的问题( dp[i][ v - c[i]] + w[i]);如果不取,dp[i][v]还是dp[i-1][v]都一样(第一次不取,以后也不会取,相当于转化成i-1件物品的问题,为了和取的情况保持一致,采用dp[i][v])。所以状态转移方程变为了dp[i][v]=max{ dp[i][v] , dp[i][ v-c[i] ] + w[i] },那么在数组里,
a b m d e
f g dp[i][v] h k
同样注意到,v-c[i]<=v,所以,每次更新dp[i][v],可能用到的值是红色部分,所以也可以压缩为一维,这里要注意了,数组中的f、g,是i下的情况,并不是i-1的情况,dp[i][v]的值取决取i下,而不是i-1,所以此时应该从前往后更新dp的值,这样才能保证取第i件物品时,dp[i][v]是由dp[i][v-c[i]]+w[i]推得,所以尽管状态转移方程仍然为dp[v]=max{ dp[v] , dp[ v-c[i] ] + w[i] },v的循环顺序却应该是从小到大。
基本上,这就是0/1背包和完全背包从二维转化为一维的思路,以后自己还要经常复习。
0/1背包的伪代码:
for i=1 to N for v=V to 0 f[v]=max{dp[v],dp[v-c[i]]+w[i]}
完全背包的伪代码:
for i=1 to N for v=0 to V f[v]=max{dp[v],dp[v-c[i]]+w[i]}
本文详细介绍了0/1背包和完全背包问题的解决方法,通过对比两种背包问题的处理方式,阐述了如何将二维动态规划转化为一维动态规划,简化了算法实现。
343

被折叠的 条评论
为什么被折叠?



