1. 程式人生 > 其它 >acwing-5. 多重揹包問題 II

acwing-5. 多重揹包問題 II

有 N 種物品和一個容量是 V 的揹包。

第 i 種物品最多有 si 件,每件體積是 vi,價值是 wi。

求解將哪些物品裝入揹包,可使物品體積總和不超過揹包容量,且價值總和最大。
輸出最大價值。

輸入格式

第一行兩個整數,N,V,用空格隔開,分別表示物品種數和揹包容積。

接下來有 N 行,每行三個整數 vi,wi,si,用空格隔開,分別表示第 i 種物品的體積、價值和數量。

輸出格式

輸出一個整數,表示最大價值。

資料範圍

0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

提示:

本題考查多重揹包的二進位制優化方法。

輸入樣例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

輸出樣例:

10

方法一:

與上一題的多重揹包問題題幹相同,資料增加了一個數量級,說明要進行優化,否則會TLE。這裡的做法是轉化成01揹包問題

對於同一類物品,只要能列舉出其[0, si]中的所有情況就可以了。假如轉化為樸素01揹包(在說了要用01揹包來做之後,這個分法是最直接能想到的),做法是同類物品拆成多個,例如s=10,分成1 1 1 1 1 1 1 1 1 1,通過其選與不選來表示出[0, si]中的所有情況,但此時複雜度為O(n * v * s),複雜度數量級達到109。考慮二進位制表示的方式,即用log(s)位數,就可以表示出[0, si]內任何數,例如s=10,只需要將物品分成1 2 4 3,通過對這幾份物品進行選與不選,就可以表示出[0, 10]內的所有數字,再例如s=20,可以分成1 2 4 8 5。因此二進位制表示的方式將複雜度優化為O(n * v * log(s))

值得學習的地方在於這種二進位制表示法,本質上可以用其它分法,只要能表示出[0, s]即可,但這種分法符合直覺,並且log的複雜度也會很低

#include <bits/stdc++.h>

using namespace std;


typedef pair<int, int> PII;
const int N = 2010;
int n, v, f[N];
vector<PII> obj;

// 轉化為樸素01揹包:同種多件物品拆解成1 1 1 1...,複雜度O(N)
// 轉化為二進位制01揹包:同種多件物品拆解成1 2 4... 餘數,複雜度O(logN)

// 本質都是01揹包,但拆解的方法可以表示[0, si]內的所有數字即可
// by yxc

int main() {
    int a, b, s;

    scanf("%d%d", &n ,&v);
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d", &a, &b, &s);
        for (int j = 1; j <= s; j *= 2) {
            s -= j;
            obj.emplace_back(a*j, b*j);
        }
        if (s) obj.emplace_back(a*s, b*s);
    }

    for (auto o : obj) {
        for (int i = v; i >= o.first; i--) {
            f[i] = max(f[i], o.second + f[i-o.first]);
        }
    }

    printf("%d", f[v]);
}