洛谷 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; }