zoukankan      html  css  js  c++  java
  • 宅男计划:单峰函数三分

    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
  • 相关阅读:
    Ubuntu 18.04 新系统 允许root远程登录设置方法
    《软件需求》读书笔记03
    第十一周总结
    python 遍历迭代器iteration与list的区别
    第九周总结
    大学生运动情况调研计划
    系统需求分析08
    系统需求分析07
    系统需求分析06
    系统需求分析05
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11425675.html
Copyright © 2011-2022 走看看