问题
给定个物品和一个容量为的背包,对于第个物品有重量和价值以及数量。选择若干个物品放入背包中,在不超过背包最大容量的情况下,求背包中物品价值之和的最大值。
过程
多重背包可以将第个物品看成个重量为,价值为的不同类物品,然后对每个物品进行0-1背包即可。
二进制分解优化:对于任何一个正整数,都可以拆解为多个2的幂次方的和。例如,1到7之间的数都可以由,,组合而成,又比如。因此可以将第个物品拆分成个物品,然后对所有物品进行0-1背包即可。
实现
一维滚动数组
int N, M;
int n;
int w[N], v[N], cnt[N];
int dp[N][M];
void knapspack()
{
for(int i = 1; i <= n; i++)
for(int k = 1; k <= cnt[i]; k++)
for(int j = W; j >= w[i]; j--) // 逆序
dp[j] = std::max(dp[j], dp[j - w[i]] + v[i]);
}二进制分解
int N, M;
struct item
{
int w, v;
} items[N];
int n;
int w[N], v[N], cnt[N];
int dp[M];
void knapspack()
{
vector<item> items;
for(int i = 1; i <= n; i++)
{
for(int k = 1; k <= cnt[i]; k *= 2)
{
items.push_back({k * w[i], k * v[i]});
cnt[i] -= k;
}
if(cnt[i] > 0) // 余数直接当做一个物品
items.push_back({cnt[i] * w[i], cnt[i] * v[i]});
}
for(item i : items)
for(int j = W; j >= i.w; j--) // 逆序
dp[j] = std::max(dp[j], dp[j - i.w] + i.v]);
}