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++;
}
}
}