Description
自从迷上了拼图,JYY 就变成了个彻底的宅男。为了解决温饱问题,JYY 不得不依靠叫外卖来维持生计。
外卖店一共有n种食物,分别由1到n编号。第i种食物有固定的价钱Pi和保质期Si。第i种食物会在Si天后过期。JYY 是不会吃过期食物的。比如 JYY 如果今天点了一份保质期为0天的食物,那么 JYY 必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了。保质期可以为天,这样这份食物就必须在购买当天吃掉。
JYY 现在有M块钱,每一次叫外卖需要额外付给送外卖小哥外送费F元。送外卖的小哥身强力壮,可以瞬间给 JYY 带来任意多份食物。
JYY 想知道,在满足每天都能吃到至少一顿没过期的外卖的情况下,他可以最多宅多少天呢?
首先,这题诡异而无聊的数据范围需要用double来计算费用,再某些特殊数据下计算数据会爆long long。
比较容易发现的就是所有的钱一部分花在了外送费上,另一部分才是买外卖的。
那么如果我们已经确定了我们要点多少次外卖,然后就可以采取最优决策了。
点外卖的次数太多不好,把太多的钱花费在小费上了。
点外卖的次数太少也不好,那样可能就会因为保质期的限制而被迫买更贵的外卖了。
奇妙的单峰函数性质。
具体为什么能看出它是单峰函数。。emm,一是直觉,二是经验。
看起来是对的,而且没有反例啊,那就是对的吧。。。好的,那它就是对的。
三分的大致套路就是就是把函数劈成3个部分,取4个端点根据大小关系来缩小区间。
目前区间的端点是l,r,你要分出的两个新端点为ml,mr。
两种分法:ml=l+(r-l)/3;mr=l+(r-l)*2/3;或ml=l+r>>1;mr=ml+1;
本质是一样的,后者的复杂度更低为2为底的对数,前者的底是1.5。
反正我们已经把函数搞成3段了,然后呢?
我们已经确定最大(小)值已经在[l,r]里了,那么只有3种情况(以求最大值为例):
1)在[l,ml]里,那么就有f(ml)>f(mr)>f(r)
2)在[ml,mr]里,那么有f(ml)>f(mr)>f(r)或f(l)<f(ml)<f(mr)
3)在[mr,r]里,那么有f(l)<f(ml)<f(mr)
所以我们可以发现如果满足f(l)<f(ml)<f(mr)那么答案一定不在[l,ml]里
如果满足f(ml)>f(mr)>f(r)那么答案一定不在[mr,r]里。
与f(l),f(r)的比较没有意义,所以我们关注的就是f(ml),f(mr)的大小关系
根据它们的大小关系我们就可以把答案区间缩小一部分了。
为了防止区间能缩小,我们要确保区间的大小至少是4(l<r-2),最后在3个可能值里取max即可。
1 while(l<r-2){ 2 ml=l+r>>1;mr=ml+1; 3 if(f(ml)<f(mr)) l=ml; 4 else r=mr; 5 } 6 ans=max(max(f(l),f(l+1)),f(r));
三分大致框架
那么剩下的问题就是决策/计算了。
显而易见,最便宜的外卖肯定是要买的,那些保质期短而价格高的我们一个也不会买。
那么我们把外卖按价格排序,价格相同的只留下保质期最长的,价格高保质期短的排除。
那么我们就不断买当前最便宜的就可以了,买的钱数是(这个保质期-上一个的保质期)×价格×点外卖次数(可能爆long long)
那么我们在总钱数m里先减去外送费,剩下的钱不断采取上述决策,如果某一种不能全部卖完那么就能买几份买几份。
这样就可以计算出天数了。套个三分,搞定。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 #define int long long 5 struct ps{int w,t;friend bool operator<(ps a,ps b){return a.w<b.w;}}lp[205],p[205]; 6 int n,m,f,cnt,mx; 7 int Q(int tms){ 8 int lft=m-f*tms; 9 for(int i=1;i<=cnt;++i) 10 if(lft/p[i].w>=1.0*tms*(p[i].t-p[i-1].t)){ 11 if(i==cnt)return p[i].t*tms; 12 lft-=(p[i].t-p[i-1].t)*p[i].w*tms; 13 } 14 else return p[i-1].t*tms+lft/p[i].w; 15 } 16 signed main(){ 17 scanf("%lld%lld%lld",&m,&f,&n); 18 for(int i=1;i<=n;++i)scanf("%lld%lld",&lp[i].w,&lp[i].t),lp[i].t++; 19 sort(lp+1,lp+1+n); 20 for(int i=1;i<=n;++i)if(lp[i].t>mx)p[++cnt]=lp[i],mx=lp[i].t; 21 int l=1,r=m/f,ml,mr,lans,rans; 22 while(l<r-2){ 23 ml=l+r>>1;mr=ml+1; 24 lans=Q(ml);rans=Q(mr); 25 if(lans<rans)l=ml;else r=mr; 26 } 27 printf("%lld\n",max(max(Q(l),Q(r)),l+1<=r?Q(l+1):0)); 28 }
完整代码811B
原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11425675.html