浅谈背包问题
问题原型:
有一个背包,总体积为V,现有N个物品,每个物品都有一个体积Vi与价值Wi,问如何选择物品,使得在总体积不超过V的前提下价值最大。
类型1:0/1背包
限制条件:每个物品只有一个
朴素做法:
经典的动态规划问题,设置$$F(i,j)$$为遍历到第i个物品,包内体积为j能获得的最大价值。
那么对于每个物品,可以选也可以不选。
所以则有:$$F(i,j) = max(F(i-1,j),F(i-1,j-v[i])+w[i]) $$
优化:
1.滚动数组:
我们发现,$$F(i)$$只能从$$F(i-1)$$转移,我们可以利用滚动数组来优化空间复杂度。
即 $$F(i%2,j) = max(F((i-1)%2,j),F((i-1)%2,j-v[i])+w[i]))$$
可将空间复杂度从NV降低至V
2.优化枚举顺序与状态设置:
我们可设置一个新的状态:dp(i) ,表示体积为i时的最大价值
那么则有:$$dp(j) = max(dp(j),dp(j-v[i])+w[i])$$
而对于枚举顺序,我们发现:
若仍是正序枚举体积,则有可能调用到已选完物品$$i$$的状态,不符合0/1背包的性质。
所以为
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
}
这样,我们也可以把空间复杂度降为V级别
类型2:完全背包
限制条件:每种物品无限多个
朴素做法:
虽然物品有无限多个,但对于每个物品,他能被选择的次数不会超过$$frac{V}{Vi}$$ 次,转成0/1背包,时间复杂度为$$O(V*sum_{i=1}^nfrac{V}{Vi})$$
优化:
在0/1背包的第二种优化中,我们为了避免一个物品被重复选择,选择了倒序枚举,而在完全背包中,物品可被多次选择,所以可以直接正向枚举。时间复杂度为$$O(V*N)$$
类型3:多重背包
限制条件:物品$$i$$的数量为$$Ci$$
朴素做法:
将多个相同物品视为不同物品,再做0/1背包,总时间复杂度为$$O(V*sum_{i=1}^nCi)$$
优化:
对于每个Ci,进行二进制分组。即每个物品分为若干种$$2k$$个物品的和。由于二进制的性质,分组后可表示0到Ci的所有数字。时间复杂度为$$O(V*sum_{i=1}nlog Ci)$$
类型4:分组背包
限制条件:给定N组物品,其中第i组有Ci个物品,每组只能选择一种物品。
朴素做法:
将最外行枚举物品改为枚举组别
优化:
压缩状态的第一维,即:
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j++)
{
for(int k=1;k<=c[i];k++)
{
if(j>=v[i][k])
f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}