3874/2832: [Ahoi2014&Jsoi2014]宅男计划

本文介绍了一种通过优化算法来解决外卖点餐问题的方法。利用单调队列预处理食品的价格和保质期,并采用三分法搜索最佳购买策略,确保在有限预算下最大化连续用餐天数。同时提供了一个随机解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目大意:外卖店有N种食物,第i种食物有固定的价钱Pi和保质期Si。第i种食物会在Si天后过期。JYY是不会吃过期食物的。
比如JYY如果今天点了一份保质期为1天的食物,那么JYY必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了。保质期可以为0天,这样这份食物就必须在购买当天吃掉。
JYY现在有M块钱,每一次叫外卖需要额外付给送外卖小哥外送费F元。送外卖的小哥身强力壮,可以瞬间给JYY带来任意多份食物。JYY想知道,在满足每天都能吃到至少一顿没过期的外卖的情况下,他可以最多宅多少天呢

题解:单调队列搞一搞,得到一个价格和保质期单调增的序列

大胆猜测(看题解)后可以YY出存活天数是关于购买次数的单峰函数……然后三分购买次数……

接下来YY一个贪心的check算法

神奇结论:每次叫外卖间隔时间越平均越优,设叫外卖次数为k,从便宜到贵买,给k次外卖都买一个当前物品,然后就扩充了叫外卖间隔时间,若钱不够买k次则能买多少买多少

也可以用比较猎奇的随机做法

我的收获:强啊

#include <cstdio>
#include <cstring>
#include <algorithm>
#define inc(i,j,k) for(int i=j;i<=k;i++)
#define ll long long
#define maxn 300
using namespace std;

inline ll read(){
    char ch=getchar(); ll f=1,x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return f*x;
}
struct nd{ll p,s;}; nd a1[maxn],a2[maxn];
ll p[maxn],s[maxn],m,f,l,r,ans; int n;
bool cmp(nd a,nd b){return a.s==b.s?a.p>b.p:a.s<b.s;}
ll calc(ll k){
    ll cost=m-k*f,day=0,ans=0;
    inc(i,1,n){
        if(a2[i].s>=day){
            ll a=min(cost/a2[i].p/k,a2[i].s-day+1); day+=a; ans+=a*k; cost-=a*k*a2[i].p;
        }
        if(a2[i].s>=day){
            ll a=min(k,cost/a2[i].p); day++; ans+=a; cost-=a*a2[i].p;
        } 
    }
    return ans;
}
int main(){
    m=read(); f=read(); n=(int)read(); inc(i,1,n)a1[i].p=read(),a1[i].s=read(); sort(a1+1,a1+1+n,cmp);
    r=1; a2[r]=a1[1];
    inc(i,2,n){while(r&&a2[r].p>a1[i].p)r--; a2[++r]=a1[i];} n=r;
    l=1; r=(m/(f+a2[1].p)); ans=0;
    while(l<=r){
        ll mid1=l+(r-l)/3,mid2=r-(r-l)/3,c1=calc(mid1),c2=calc(mid2);
        if(c1<c2)ans=max(ans,c2),l=mid1+1;
        else if(c1>c2)ans=max(ans,c1),r=mid2-1;
        else ans=max(ans,c1),l=mid1+1,r=mid2-1;
    }
    printf("%lld",ans); return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值