宅男计划:单峰函数三分

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
",max(max(Q(l),Q(r)),l+1<=r?Q(l+1):0));
28 }
完整代码811B
原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11425675.html