acwing-5. 多重揹包問題 II
阿新 • • 發佈:2022-03-14
有 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]); }