解释
完全背包模型与 0-1 背包类似,与 0-1 背包的区别仅在于一个物品可以选取无限次,而非仅能选取一次。
我们可以借鉴 0-1 背包的思路,进行状态定义:设 fi,j 为只能选前 i 个物品时,容 量为 j 的背包可以达到的最大价值。
需要注意的是,虽然定义与 0-1 背包类似,但是其状态转移方程与 0-1 背包并不相同。
过程
可以考虑一个朴素的做法:对于第 i 件物品,枚举其选了多少个来转移。这样做 的时间复杂度是 O(n³) 的。
状态转移方程如下:
考虑做一个简单的优化。可以发现,对于 fi,j,只要通过 fi,j−wi 转移就可以了。因 此状态转移方程为:
理由是当我们这样转移时,fi,j−wi 已经由 fi,j−2×wi 更新过,那么 fi,j−wi 就是充分 考虑了第 i 件物品所选次数后得到的最优结果。换言之,我们通过局部最优子结 构的性质重复使用了之前的枚举过程,优化了枚举的复杂度。
与 0-1 背包相同,我们可以将第一维去掉来优化空间复杂度。如果理解了 0-1 背包的优化方式,就不难明白压缩后的循环是正向的(也就是上文中提到的错误优化)。
例题
题意概要:有n种物品和一个容量为w的背包,每种物品有重量wi和价值vi两种属性,要求选若干个物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。
#include <iostream>
using namespace std;
const int maxn = 1e4 + 5;
const int maxW = 1e7 + 5;
int n, W, w[maxn], v[maxn];
long long f[maxW];
int main() {
cin >> W >> n;
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++)
for (int l = w[i]; l <= W; l++)
if (f[l - w[i]] + v[i] > f[l]) f[l] = f[l - w[i]] + v[i]; // 核心状态方程
cout << f[W];
return 0;
}
下一篇讲多重背包问题。