多重背包的二进制优化
以一道题目举例:
来源:宁波工程学院2020年新生校赛
题目描述:
训练师小梁在一次机缘巧合中,发现了一个皮卡丘部落,她非常喜欢皮卡丘,但由于精灵球有限,所以她打算在这里逗留一段时间,部落中有n个皮卡丘,每个皮卡丘有不同的可爱度q[i],小梁要欣赏这些皮卡丘,但有的皮卡丘被看多了会抑郁,所以她要合理的分配时间和看的次数,收获最多的可爱度。
输入:
到达部落的时间s(英文的冒号),离开部落的时间e(英文的冒号),皮卡丘的个数n (s<=e,n<=10000) 下面n行
t,q,s,分别代表,欣赏这只皮卡丘需要的时间(分钟),这只皮卡丘的可爱度,这只皮卡丘最多能看几次(s=0表示这只皮卡丘脾气很好,能看无限次)。
输出:
能获取的最大可爱度。
样例输入:
5:30 7:10 5
3 1 5
4 4 2
2 1 0
4 5 3
5 6 0
样例输出:
120
代码:
#include <iostream>
using namespace std;
const int INF = 65535;
int n, m, n1;
int count;
int v[100001], w[100001];
int f[100001];
void read() {
int a = 0, b = 0, c = 0, d = 0;
int i;
scanf("%d:%d%d:%d%d", &a, &b, &c, &d, &n);
if (a == c) m = d - b;
else if (a < c && b > d) m = (c - a - 1) * 60 + (d + 60 - b);
else m = (c - a) * 60 + (d - b);
for (i = 1; i <= n; i++) {
int x, y, s, t = 1;
scanf("%d%d%d", &x, &y, &s);
if (s == 0) s = INF; //如果是0,表示有无限件
//二进制优化,总所周知,二进制可以用来表示任何数,比如10以内的数:
//10 = 1 + 2 + 4 + 3, 这四个数字就可以用来表示10包括10以内的所有数字,比原来的10个数字少了6个
while (s >= t) {
v[++n1] = x * t; //体积
w[n1] = y * t; //价值
s -= t;
t *= 2;
/*
循环过程:
0 s = 10, t = 1;
1 s = 9, t = 2;
2 s = 7, t = 4;
3 s = 3, t = 8;
*/
}
//这一步是二进制没有补到的数,比如10 = 1 + 2 + 4 + 3,这一步就是加上3的部分
v[++n1] = x * s;
w[n1] = y * s;
}
}
int main() {
read();
//01背包
for (int i = 1; i <= n1; 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;
}
代码中使用了二进制优化,关于多重背包的二进制优化还可以参考阎总的背包九讲专题,大概在56:00处:背包九讲专题
普通多重背包二进制优化模板:
#include <iostream>
using namespace std;
int n, n1, m;
int v[1001], w[1001], f[1001];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int x, y, s, t = 1;
cin >> x >> y >> s;
while (s >= t) {
v[++n1] = t * x;
w[n1] = t * y;
s -= t;
t *= 2;
}
v[++n1] = s * x;
w[n1] = s * y;
}
for (int i = 1; i <= n1; 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;
}