1.将多重背包转化成01背包求解。
即将物品的数量M按照二进制分解成 M = 1 + 2 + 4 + ... + 2 ^ k + a. 然后对这些物品进行01背包。
如果没有求具体方案的,我们可以在求解的过程中进行分解,而不保留对应的物品。
具体代码如下:
for(int i = 0; i < N; ++i){
int num = m[i];
for(int k = 1; num; k <<= 1){
int mul = min(k,num);
for(int j = W; j >= w[i] * mul; --j)
dp[j] = max(dp[j],dp[j - w[i] * mul] + v[i] * mul);
num -= mul;
}
}
2.用单调队列优化多重背包
考虑最原始的多重背包的转移方程:dp[i+1][j] = max(dp[i][j-k * w[i]] + k * v[i] | 0 <= k <= m && j - k * w[i] >= 0)
可以注意到,对于不同的j ,若j mod w[i]的值不一样,则他们之间是相互独立的。
我们先考虑j mod w[i] = 0的情况。我们定义 a[j] = dp[i][j * w[i]].
这样转移方程可以写成:
dp[i+1][(j+k) * w[i]] = max(a[j + k * v[i]],a[j+1] + (k-1) * v[i],...,a[j'] + (j+k-j')) * v[i],...,a[j+k].
但是直接还是无法方便地计算,再做如下的变形 b[j] = a[j] - j * v[i];
dp[i+1][(j+k) * w[i]] = max(b[j],b[j+1],..b[j+k]) + (j+k)* v[i];
这样,就是滑动最小值一样,可以单调队列优化了。
代码如下:
for(int i = 0; i < N; ++i){
for(int a = 0; a < w[i]; ++a){
int s = 0, t = 0;
for(int j = 0; j * w[i] + a <= W; ++j){
int val = dp[j * w[i] + a] - j * v[i];
while(h < t && deqv[t - 1] <= val) t--;
deq[t] = j , deqv[t++] = val;
dp[j * w[i] + a] = deqv[s] + j * v[i];
if(deq[s] == j - m[i]) s++;
}
}
}