正解:二进制拆分
前言:一直以为背包中的二进制拆分很NB而且很难的样子,知道我今天学到了之后,发现???
题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间Ti。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入格式
共n+1行:
第1行:三个数:现在时间Ts(几点:几分),去上学的时间Te(几点:几分),爱与愁大神院子里有几棵樱花树n。
第2行~第n+1行:每行三个数:看完第i棵树的耗费时间Ti,第i棵树的美学值Ci,看第i棵树的次数Pi(Pi=0表示无数次,Pi是其他数字表示最多可看的次数Pi)。
输出格式
只有一个整数,表示最大美学值。
输入输出样例
输入 #1
6:50 7:00 3
2 1 0
3 3 1
4 5 4
输出 #1
11
说明/提示
100%数据:Te-Ts ≤ 1000,n ≤ 10000
样例解释:赏第一棵樱花树一次,赏第三棵樱花树2次
正解:二进制拆分
- 我们很快就可以得到一个正常思路,每次动态规划到一种樱花,如果能看的次数为无限次,那么我们就用完全背包来对待,否则我们就用多重背包来对待
预估得分:100pts
实际得分:90pts
最后一个数据1.03s
考虑优化:加个快读(我唯一想出的优化 )
预估得分:100pts
实际得分:90pts
最后一个数据1.05s
比前面还慢,说好的超级无敌屌炸天快读呢梗出处
二进制拆分
做法:把每一个物品根据2的多少次方拆分,因为任何数都可以转化为二进制数
核心思想:把每一个物品拆成很多个,分别计算价值和所需时间,再转化为01背包求解
最后一点:完全背包可以把他的空间记为999999,不要太大,一般百万就足够了
AC代码
#include<cstdio>
#include<algorithm>
#define re register int
using namespace std;
int nx,ny,ex,ey,n,f[1010];
int a[10005],b[10005],c[10005];
int tp,co[1000005],v[1000005];//尽可能开大,不要把空间开爆了
inline void pre() {
for(re i=1;i<=n;i++) {
int t=1;
while(c[i]) {
co[++tp]=t*a[i];
v[tp]=t*b[i];
c[i]-=t; t*=2;
if(c[i]<t) {//如果剩下的不能再拆,就直接放在一起
co[++tp]=a[i]*c[i];
v[tp]=b[i]*c[i];
break;
}
}
}
}
int main() {
scanf("%d:%d%d:%d%d",&nx,&ny,&ex,&ey,&n);
int t=(ex*60+ey)-(nx*60+ny);
for(re i=1;i<=n;i++) {
scanf("%d%d%d",&a[i],&b[i],&c[i]);
if(!c[i]) c[i]=999999;
}
pre();//二进制拆分
for(re i=1;i<=tp;i++) {//考虑每个拆出来的物品
for(re j=t;j>=co[i];j--)//01背包板子
f[j]=max(f[j],f[j-co[i]]+v[i]);
}
printf("%d",f[t]);
return 0;
}