洛谷 P1833 樱花

话不多说先看题板:

题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间Ti。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入格式
共n+1行:
第1行:三个数:现在时间Ts(几点:几分),去上学的时间Te(几点:几分),爱与愁大神院子里有几棵樱花树n。
第2行~第n+1行:每行三个数:看完第i棵树的耗费时间Ti,第i棵树的美学值Ci,看第i棵树的次数Pi(Pi=0表示无数次,Pi是其他数字表示最多可看的次数Pi)。
输出格式
只有一个整数,表示最大美学值。

  首先这题是个完全背包+01背包,完全背包就是pi=0的情况,看多次可以想象成有多棵樱花树都是一样的耗时,一样的美丽,还都只能看一次。好了这就是01背包。

感觉这个方法很有道理,写了。然后现实就给了我一个耳光。看看这个耳光他又大又响:

由于我这些题是去xiaoxx的博客园看到的,于是我去看了看他是怎么做的(无耻行为),附上传送门

然后他用了一种很奇妙的做法(?)

他的手法…是卡了我3个月的…倍增(?)

哎哟真是精妙的手法!把可以看多次分成了n个数,每个的美观和耗时相当于原来的2^i倍。这样就相当于省略了许多计算次数啊!大家都知道从2的1次方开始到第i次方之间的所有2的次方数可以把2^(i+1)以下所有的数全部概括。这就让原本需要循环pi次的循环直接变成shu次了(2^shu<=pi,2^(shu+1)>pi)。真是机智啊!(不说了我去点个赞)

新知识get

(特别感谢xiaoxx大佬助我完成此题)

要开始上被改的很乱的代码了:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n;
long long dp[10005];
long long t[10005],mx[10005],cs[10005];
long long h1,m1,h2,m2,m;
long long ans,shu[100005],sj[100005],zcs;
int main()
{
	scanf("%lld:%lld %lld:%lld %lld",&h1,&m1,&h2,&m2,&n);
	m=(h2-h1)*60+(m2-m1);//朴实无华的计算时间 
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",&t[i],&mx[i],&cs[i]);//t是时间,mx是美学,cs是次数(因为之前的奇怪写法导致我用数组存…) 
	}
	for(int i=1;i<=n;i++)
	{
		if(cs[i]==0)//这个是无数次,就随便定义一个不可能的值吧 
		{
			cs[i]=100000;
		}
		for(int j=1;j<=cs[i];j*=2)//开始倍增(再次膜拜xiaoxx大佬) 
		{
			ans++;
			shu[ans]=mx[i]*j;
			sj[ans]=t[i]*j;
			cs[i]-=j;
		}
		if(cs[i]!=0)//剩下一点点,直接用剩下的当倍数,没毛病 
		{
			ans++;
			shu[ans]=mx[i]*cs[i];
			sj[ans]=t[i]*cs[i];
		}
	}
	for(int i=1;i<=ans;i++)//再次朴实无华的01背包 
	{
		for(int u=m;u>=sj[i];u--)
		{
			dp[u]=max(dp[u],dp[u-sj[i]]+shu[i]);
		}
	}
	printf("%lld\n",dp[m]);//完结撒花 
	return 0;
}

  

posted @ 2020-07-14 18:05  lichangjian  阅读(278)  评论(0编辑  收藏  举报