洛谷 P1833 樱花 背包+二进制拆分

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_44343213/article/details/102667752

正解:二进制拆分

前言:一直以为背包中的二进制拆分很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;
} 

猜你喜欢

转载自blog.csdn.net/qq_44343213/article/details/102667752