1. 程式人生 > 其它 >AcWing 6 多重揹包問題III

AcWing 6 多重揹包問題III

\(n\)個物品, 揹包容量為\(m\)

假設第\(i\)個物品體積為\(v\), 價值為\(w\), 個數為\(s\)

f[i, j] = f[i-1, j-v]+w, f[i-1, j-2v]+2w, f[i-1, j-3v] +3w,  f[i-1, j-4v]+4w,  ... f[i-1,j-sv]+sw
f[i, j-v] =              f[i-1, j-2v]+w,  f[i-1,j-3v] +2w,  f[i-1, j-4v]+3w, ... f[i-1, j-(s+1)v] + sw
f[i, j-2v] =                              f[i-1, j-3v]+w ,  f[i-1, j-4v]+2w, ...f[i-1, j-(s+2)v] + sw

設m % v = d 變換下上面式子可以得出,

f[i, d] = f[i-1][d]
f[i, d+v] = max(f[i-1, d] +w, f[i-1, d+v]) = max(f[i-1, d], f[i-1, d+v]-w) + w
f[i, d+2v] = max(f[i-1, d] +2w, f[i-1, d+v] +w, f[i-1, d+2v]) = max(f[i-1, d], f[i-1, d+v]-w, f[i-1, d+2v]-2w) + 2w
f[i, d+3v] = max(f[i-1, d] +3w, f[i-1, d+v] +2w, f[i-1, d+2v]+w, f[i-1, d+3v]) = max(f[i-1, d], f[i-1, d+v]-w, f[i-1, d+2v]-2w, f[i-1, d+3v]-3w) + 3w
f[i, d+4v] = max(f[i-1, d] +4w, f[i-1, d+v] +3w, f[i-1, d+2v]+2w, f[i-1, d+3v]+w, f[i, d+4v])

我們發現對於體積 j,j % v = d的話,j的狀態僅由體積 % v 也等於d的狀態轉移而來, 比如d+3v, 僅由體積為d+v, d+2v, d這些狀態轉移, 這些體積 % v都等於d,我們將之前的狀態
放入單調佇列即可, 放入時,有個技巧,放入 f[i-1, d+ jv] - jw 而不是 f[i-1, d+ jv] ,看看上面的式子就知道為啥了,是為了利用之前的結果。 我們減去再加回來就能保證當前狀態最後的答案正確了。

第0次 f[i, d] = f[i-1, d] 入隊
第1次 f[i-1, d+v]-w 進佇列與佇列之前的數字比較
第2次 f[i-1, d+2v]-2w 進佇列與佇列之前的數字比較
第j次 f[i-1, d+jv]-jw 進佇列與之前佇列中的數字比較

所以我們列舉i個物品
列舉餘數d
列舉j即可, 用於判斷體積為d + jv的時候,揹包能裝滿的最大值 由上面式子發現可以用單調佇列處理重複項

有個小細節,由於物品個數有限制我們要加個陣列儲存 佇列中狀態對應的編號j

#include <bits/stdc++.h>
using namespace std;
const int C_N = 1005, C_V = 20005;
int f[C_N][C_V], q[C_V], num[C_V];

int main() {
    int N, V;
    cin >> N >> V;
    for (int i = 1; i <= N; i++) {
        int v, w, s;
        cin >> v >> w >> s;
        for (int d = 0; d < v; d++) {
            int head = 0, tail = -1;
            for (int j = 0; j <= (V - d) / v; j++) {
                int tmp = f[i - 1][d + j * v] - j * w;
                while (head <= tail && j - num[head] > s) head++;
                while (head <= tail && q[tail] <= tmp) tail--;
                q[++tail] = tmp;
                num[tail] = j;
                f[i][d + j * v] = q[head] + w * j;
            }
        }
    }
    cout << f[N][V];
    return 0;
}