题意:外卖店一共有N种食物,分别有1到N编号。第i种食物有固定的价钱Pi和保质期Si。第i种食物会在Si天后过期。JYY是不会吃过期食物的。比如JYY如果今天点了一份保质期为1天的食物,那么JYY必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了。保质期可以为0天,这样这份食物就必须在购买当天吃掉。JYY现在有M块钱,每一次叫外卖需要额外付给送外卖小哥外送费F元。送外卖的小哥身强力壮,可以瞬间给JYY带来任意多份食物。JYY想知道,在满足每天都能吃到至少一顿没过期的外卖的情况下,他可以最多宅多少天呢?
看了WNJXYK的题解,感觉太强了。这题如果DP可以做到60分,但M达到10^18就处理不了了。首先有一个贪心,对于食物i,j,如果si>=sj,pi<=pj,那么j显然无用,删去。接下来按照保质期排序,根据我们前面的操作,现在一定是保质期越小价格越低,所以从保质期小的开始取,能多买则多买,这样一定是最优的。但现在还有一个问题,可以买多次,但题目买的次数和生存天数之间有二次函数的关系(别问我,我也不知道怎么证),所以三分买的次数,计算答案。
Tips:1、因为可以买多次,贪心的时候要注意可能有食物在一些次数中可以买,另一些不能买(花费除以次数*价格可能有剩余),但这种情况只会出现在最后一种购买的食物中,所以要特别判断一下。
2、到处开long long(我查了好久)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=200+10;
long long m,f;
int n,del[maxn],tot;
struct node
{
long long p,s;
}a[maxn],hm[maxn];
int cmp(node a,node b)
{
if(a.p!=b.p) return a.p<b.p;
else return a.s<b.s;
}
int cmp1(node a,node b)
{
return a.s<b.s;
}
long long calc(long long k)
{
long long cost=m-f*k,day=0,res=0;
for(int i=1;i<=n;i++) //尽量买保质期小的
{
if(a[i].s>=day)
{
long long num=min((cost/a[i].p)/k,a[i].s-day+1);
day+=num;res+=num*(long long)k;
cost-=num*a[i].p*k;
}
if(a[i].s>=day)
{
long long v=min(k,cost/a[i].p);
day++;res+=v;
cost-=a[i].p*v;
}
}
return res;
}
int main()
{
//freopen("food.in","r",stdin);
//freopen("food.out","w",stdout);
scanf("%lld%lld%d",&m,&f,&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&hm[i].p,&hm[i].s);
long long mini=hm[1].p;
sort(hm+1,hm+n+1,cmp);
for(int i=1;i<=n;i++)
{
int now=i;mini=min(mini,hm[i].p);
while(i+1<=n&&hm[i+1].s<=hm[now].s)
{
del[i+1]=1;
i++;
}
}
for(int i=1;i<=n;i++)
if(!del[i])
{
a[++tot].p=hm[i].p;a[tot].s=hm[i].s;
}
n=tot;
sort(a+1,a+n+1,cmp1);
long long l=1,r=m/(f+mini);
while(l+25<r)
{
long long x1=l+(r-l)/3;
long long x2=l+(r-l)*(long long)2/(long long)3;
if(calc(x1)<calc(x2)) l=x1;
else r=x2;
}
long long res=calc(l);
for(long long i=l+1;i<=r;i++)
res=max(calc(i),res);
printf("%lld\n",res);
return 0;
}