洛谷 P1833 樱花
Description
Solution
算法:多重背包
但是裸的多重背包复杂度 \(O(n*m*p)\)(\(p\) 为物品个数),过不了此题,会 \(TLE\)。
我们考虑优化。
多重背包有两种优化方法,一种是二进制拆分优化,另一种是单调队列优化
这里只介绍一种:二进制拆分优化多重背包
另一种以后有时间再补吧。
顾名思义,我们要把数进行二进制拆分,那么我们要拆分什么呢?
我们要把物品个数进行差分,例如有 19 个物品,我们要把它拆成 5个物品,每个物品个数分别为1, 2, 4,8,3。
不难证明,这 5 个的任意组合可以表示出 1 ~ 19 的任意一个数,这也是二进制拆分优化的原理。
我们把 19 拆成 5 个物品后,他们的价值和体积也要相应做出改变,即变为 个数 * 单个的价值/体积,跟总价 = 个数 * 单价一个原理。
然后我们直接对拆分后的数组跑一个01背包即可,复杂度 \(O(num * m)\),\(num\) 为拆分后的数组长度。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 10010;
int h1, m1, h2, m2, n, m, num;
int t[N], c[N], p[N], w[N * 15], v[N * 15], f[N];
void prework(){ //二进制拆分,可以看着代码理解一下
for(int i = 1; i <= n; i++){
int tmp = 1;
while(p[i]){
w[++num] = tmp * t[i];
v[num] = tmp * c[i];
p[i] -= tmp;
tmp *= 2;
if(p[i] < tmp){
w[++num] = p[i] * t[i];
v[num] = p[i] * c[i];
break;
}
}
}
}
int main(){
scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
m = (60 * h2 + m2) - (60 * h1 + m1);
for(int i = 1; i <= n; i++){
scanf("%d%d%d", &t[i], &c[i], &p[i]);
if(!p[i]) p[i] = 1e4; //赋成无穷大
}
prework();
for(int i = 1; i <= num; i++) //注意这里是num
for(int j = m; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
printf("%d\n", f[m]);
return 0;
}