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, M = 2010;
int n, m;
int v[N], w[N];
int f[M];
//多重背包的二进制优化
int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n >> m;
int cnt = 0;
for (int i = 1; i <= n; i++) {
//每行三个整数 a,b,s,用空格隔开,分别表示第 i 种物品的体积、价值和数量
int a, b, s;
cin >> a >> b >> s;
//二进制优化,能打包则打包之,1,2,4,8,16,...
int k = 1;
while (k <= s) {
cnt++;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
//剩下的
if (s > 0) {
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;//数量减少啦
//01背包
for (int i = 1; i <= n; i++)
for (int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}