1、与朴素版本的区别
区别在于数据范围变大了:现在是三个循环数据上限分别是\(1000\)(物品种数),\(2000\)(背包容积),第\(i\)种物品的体积、价值和数量的上限也是\(2000\),原来的每个数字上限都是\(100\)!
三重循环的话,计算次数就是 \(1000 * 2000 * 2000=4000000000=4 * 1e9 =40\)亿次
\(C++\)一秒可以算\(1e8\)次,就是\(1\)亿次,\(40\)亿肯定会超时!
2、二进制优化
朴素多重背包做法的本质:将有数量限制的相同物品看成多个不同的\(0-1\)背包。
优化的思路:比如我们从一个货车搬百事可乐的易拉罐(因为我爱喝不健康的快乐水~),如果存在\(200\)个易拉罐,小超市本次要的数量为一个小于\(200\)的数字\(n\),搬的策略是什么呢?
A、一个一个搬,直到\(n\)为止。
B、在出厂前打成\(64\)个一箱,\(32\)个一箱,\(16\)个一箱,\(8\)个一箱,\(4\)个一箱,\(2\)个一箱,\(1\)个一箱,最后剩下的打成\(73\)个一箱。
为什么要把剩下的\(73\)个打成一个包呢?不是再分解成\(64\),\(32\)这样的组合呢?这是因为我们其实本质是化解为\(01\)背包,一来这么分解速度最快,二来可以表示原来数量的任何子集,这样就\(OK\)了!
3、C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 12010;
const int M = 2010;
int n, m;
int v[N], w[N];
int f[M];
int k;
//多重背包的二进制优化
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int a, b, s;
cin >> a >> b >> s;
//二进制优化,能打包则打包之,1,2,4,8,16,...
int j = 1;
while (j <= s) {
k++;
v[k] = a * j;
w[k] = b * j;
s -= j;
j *= 2;
}
//剩下的
if (s > 0) {
k++;
v[k] = a * s;
w[k] = b * s;
}
}
for (int i = 1; i <= k; i++)
for (int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
printf("%d", f[m]);
return 0;
}