一.背景
0-1背包与完全背包的唯一的区别在于0-1背包每个物品只能使用一次,但是完全背包可以重复使用。
二.0-1背包空间优化
假设物品编号1~n
i号物品重量w[i]
i号物品价值v[i]
以下分析基于dp[4](新)=dp[3]+v[2] dp[5]=dp[4](旧)+v[2]
0-1背包使用的倒叙遍历就是为了避免重复使用同一个物品。
1.先从反面说明,假设使用顺序遍历,dp[4](新)=dp[3]+v[2] ,dp[5]=dp[4](新)+v[2] 将dp[4]带入dp[5]可以得到dp[5]=dp[3]+2*v[2]即会使用到2个2号物品,不满足0-1背包的要求,即每个物品只使用一次。
2.使用倒叙为什么可以?
因为我们每次先更新的都是后面的元素,当在计算dp[5]时实际的dp[4]还没有更新,还是之前的状态,这种情况下,我们就最多只能使用到一次当前的2号元素,即dp[5]=dp[4](旧)+v[2] 。假设dp[4](旧)=dp[2]+v[1](使用1号元素更新的数据) 带入dp[5]可以得到dp[5]=dp[2]+v[1]+v[2],可以发现,dp[5]中并不存在重复的情况。其他位置元素类似。
你可能会想如果是dp[4](旧)=dp[3](旧旧)+v[2](旧旧)呢,这不就又是使用了两次v[2]了吗,但是实际上我们每次都是取的一个元素然后判断它放的位置,所以v[2]的使用必然在一次循环中,而我们使用的倒序,是先从后面开始的所以在dp[5]使用v[2]之前,dp[4](旧)是不可能使用到v[2]的,实际使用的v[2]旧旧(即v[2]旧的上一层)。
三.完全背包空间优化
同样假设物品编号1~n
i号物品重量w[i]
i号物品价值v[i]
以下分析基于dp[4]最优=dp[3]+v[2] dp[5]=dp[4](新)+v[2]
为什么使用顺序遍历优化?
顺序遍历的特点是,我们可以对同一物品使用多次。比如0-1背包的例子:dp[4](新)=dp[3]+v[2] ,dp[5]=dp[4](新)+v[2] 。值得注意的一点,顺序遍历不是一定要使用多次,而是允许我们使用多次(实际可以为0~无数次)。另外使用多次是建立在其他元素之上如dp[5]是建立在dp[4]之上,因为dp[4]使用了一次物品2才有了dp[5]共使用了2次物品2。如果不是建立在其他元素之上,而是直接加上多少次=》eg:k*v[2] 这属于暴力的完全背包解法。
四.模板
0-1背包:
for(int i=1;i<=n;i++{
for(int j=m;j>=w[i];j–){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
完全背包:
for(int i=1;i<=n;i++){
for(int j=w[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}