原题链接:https://www.luogu.com.cn/problem/P1833
樱花
题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了 nnn 棵樱花树,每棵都有美学值 CiC_iCi 。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 AiA_iAi 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 TiT_iTi 。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入格式
共 n+1n+1n+1行:
第 111 行:现在时间 TsT_sTs(几时:几分),去上学的时间 TeT_eTe(几时:几分),爱与愁大神院子里有几棵樱花树 nnn。这里的 TsT_sTs,TeT_eTe 格式为:hh:mm,其中 0≤hh≤230 \leq hh \leq 230≤hh≤23,0≤mm≤590 \leq mm \leq 590≤mm≤59,且 hh,mm,nhh,mm,nhh,mm,n 均为正整数。
第 222 行到第 n+1n+1n+1 行,每行三个正整数:看完第 iii 棵树的耗费时间 TiT_iTi ,第 iii 棵树的美学值 CiC_iCi ,看第 iii 棵树的次数 PiP_iPi(Pi=0P_i=0Pi=0 表示无数次,PiP_iPi 是其他数字表示最多可看的次数 PiP_iPi)。
输出格式
只有一个整数,表示最大美学值。
输入输出样例
输入 #1
6:50 7:00 3
2 1 0
3 3 1
4 5 4
输出 #1
11
说明/提示
100%100\%100% 数据:Te−Ts≤1000T_e-T_s \leq 1000Te−Ts≤1000(即开始时间距离结束时间不超过 100010001000 分钟),n≤10000n \leq 10000n≤10000。保证 Te,TsT_e,T_sTe,Ts 为同一天内的时间。
样例解释:赏第一棵樱花树一次,赏第三棵樱花树 222 次。
题解
退役老咸鱼含泪复习多重背包及其二进制拆分。
多重背包即指一个背包可以反复使用有限次,朴素的做法是这个背包能用多少次就做几次010101背包,若背包个数为nnn,总容量为VVV,每个背包能被重复使用的次数为pip_ipi,那么这个算法的复杂度为O(n×V×∑pi)O(n\times V\times \sum p_i)O(n×V×∑pi),这个∑pi\sum p_i∑pi就显得十分恐怖。
对于一个可以使用ppp次的背包,我们并不需要将其拆分为ppp个背包,实际上存在更简单的拆分方法,就是二进制拆分。
我们可以将拆分一个可以使用ppp次背包的问题简化为:将数ppp拆分为若干个数之和,使0∼p0\sim p0∼p中的所有数都能由被拆分出的数相加得到,二进制拆分就能解决这个问题。
例如:将131313拆分为1,2,4,61,2,4,61,2,4,6就能通过这四个数之间的加和得到0∼130\sim 130∼13之间的所有数。
为什么是二进制?因为对于拆分出的数,加和时只有“加”与“不加”这两个选项;对于一个二进制数,它的每一位也只有“000”和“111”这两个选项,这两者之间有很好的对应关系,所以一堆222的幂:1,2,4,⋯ ,2n−11,2,4,\cdots,2^{n-1}1,2,4,⋯,2n−1就可以表示出0∼2n−10\sim 2^n-10∼2n−1中的所有数。
但是直接将ppp拆分为一堆222的幂也存在问题,例如对于131313:若拆分为1,2,41,2,41,2,4,则只能表示出0∼70\sim 70∼7;若拆分为1,2,4,81,2,4,81,2,4,8,则会表示到0∼150\sim 150∼15,上述两种拆分都不是我们想要的结果。
从上面的例子可以看到,131313的正确拆分为1,2,4,61,2,4,61,2,4,6,666为131313在拆去1,2,41,2,41,2,4后剩余的部分,所以正确的拆分姿势为:从111开始将该数先拆分为递增的222的幂,直到无法继续拆分时将剩下的部分单独作为拆分出的一个数。
以下为该拆分正确性的简要说明:
首先,由于1,2,41,2,41,2,4的存在,0∼70\sim 70∼7中的所有数便都可以表示出来。
如果我们要凑出8∼138\sim 138∼13中的数,不妨反过来想,当我们把所有拆分出的数加在一起时和为131313,要凑出8∼138\sim 138∼13就是从中减去一个范围在0∼50\sim 50∼5的数,显然用1,2,41,2,41,2,4可以轻松凑出0∼50\sim 50∼5,把他们从全集中拿掉,我们便得到了8∼138\sim 138∼13。
由此,我们便完成了二进制拆分,因为我们是以倍增的方式拆分,所以一个可以用ppp次的背包拆出来的背包数量大约为log2plog_2^plog2p个,大大优化了复杂度。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+5;
int hs,he,ms,me,n,cot,tim,ans;
int dp[1005];
struct bag{int t,c;bool inf;}flo[M];
void div(int a,int b,int c)
{
for(int i=1;c>=i;c-=i,i<<=1)flo[++cot]=(bag){a*i,b*i,0};
flo[++cot]=(bag){a*c,b*c,0};
}
void in()
{
scanf("%d:%d%d:%d%d",&hs,&ms,&he,&me,&n);
for(int i=1,a,b,c;i<=n;++i)
{
scanf("%d%d%d",&a,&b,&c);
if(c)div(a,b,c);
else flo[++cot]=(bag){a,b,1};
}
}
void ac()
{
tim=he*60+me-hs*60-ms;
dp[0]=1;
for(int i=1;i<=cot;++i)
{
if(flo[i].inf){for(int j=0;j<=tim;++j)if(j+flo[i].t<=tim&&dp[j])dp[j+flo[i].t]=max(dp[j+flo[i].t],dp[j]+flo[i].c);}
else for(int j=tim;j>=0;--j)if(j+flo[i].t<=tim&&dp[j])dp[j+flo[i].t]=max(dp[j+flo[i].t],dp[j]+flo[i].c);
}
for(int i=0;i<=tim;++i)ans=max(ans,dp[i]);
printf("%d",ans-1);
}
int main(){in(),ac();}

本文介绍了一道关于在限定时间内,通过选择特定樱花树观赏次数以最大化美学值的算法问题。通过对多重背包问题的二进制拆分优化,实现高效算法解决方案。
2813

被折叠的 条评论
为什么被折叠?



