Bzoj2832 / Bzoj3874 宅男小C
Submit: 124 Solved: 26
Description
众所周知,小C是个宅男,所以他的每天的食物要靠外卖来解决。小C现在有M元钱,他想知道这些钱他最多可以吃多少天。
餐厅提供N种食物,每种食物有两个属性,单价Pi和保质期Si,表示小C需要花Pi元才能买到足够一天吃的这种食物,并且需要在送到Si天内吃完,否则食物会变质,就不能吃了,若Si为0则意味着必须在送到当天吃完。另外,每次送餐需要额外F元送餐费。
Input
每个测试点包含多组测试数据;
每个测试数据第一行三个整数M,F,N,如题目描述中所述;
以下N行,每行两个整数,分别表示Pi和Si。
Output
对于每个测试数据输出一行,表示最多可以吃的天数。
Sample Input
32 5 2
5 0
10 2
10 10 1
10 10
10 1 1
1 5
5 0
10 2
10 10 1
10 10
10 1 1
1 5
Sample Output
3
0
8
0
8
HINT
【数据规模及约定】
对于40%的数据,M,Si <= 2*10^6;
对于100%的数据,M, Si<= 10^18,1 ≤ T ≤ 50,1 ≤ F ≤ M,1 ≤ N ≤ 200,1 ≤ Pi ≤ M。
Source
死宅真恶心(嫌弃脸)
嗯?你问我的博客背景?
……那是我朋友们的照片啊,怎么就和四斋扯上关系了?(不满)
双倍经验。3874的数据比2832弱得多。
数学问题(?) 三分法 贪心
首先去掉那些没用的食物(比某种食物更贵且保质期更短)。
看到最多天数,不妨试试二分。然而二分并不会做,随意脑洞一下发现这应该是个单峰函数,可以三分答案。
因为食物可以重复买,每次买的最优决策肯定是一样的,可以放在一起处理。
三分购买的次数lim,check此时的花费,将食物按价格从小到大排序,在保质期内贪心购买,如果保质期不够了,就换用更贵但更持♂久的食物,看最多能撑多少天。
在解决了几个NC错误和一个爆int错误以后成功A掉了3874
然而在2832WA掉了。
discuss里说这并不是一个严格的单峰函数,在逼近极值的时候函数曲线可能会有波动,如果将剩余钱数作为第二关键字,就是严格的单峰函数了。
虽然不是很懂,但是姑且照这么写了,可以过。
看到网上还有三分+模拟退火的写法,不由感叹随机大法好
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #define LL long long 7 using namespace std; 8 const int mxn=230; 9 LL read(){ 10 LL x=0,f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return x*f; 14 } 15 LL M,F; 16 int n,cnt=0; 17 struct fd{ 18 LL p,s; 19 bool operator < (const fd &b)const{ 20 return p<b.p; 21 } 22 }a[mxn]; 23 LL check(LL lim,LL &left){ 24 LL res=0,now=0; 25 if(F*lim>=M){left=0;return 0;} 26 left=M-F*lim; 27 for(int i=1;i<=n;i++){ 28 if(a[i].s>=now){//lim次都买 29 LL tmp=min(a[i].s-now+1,left/(a[i].p*lim));//可以买的天数*lim 30 now+=tmp;//单次生存天数 31 res+=tmp*lim;//总生存天数 32 left-=a[i].p*tmp*lim; 33 } 34 if(a[i].s>=now){//零买 35 LL tmp=min((LL)lim,left/a[i].p); 36 ++now; 37 res+=tmp; 38 left-=a[i].p*tmp; 39 } 40 if(left<a[i].p)break; 41 } 42 return res; 43 } 44 void solve(){ 45 LL l=1,r=M/(F+a[1].p),L1,L2; 46 LL ans=0; 47 while(l<=r){ 48 LL mL=l+(r-l)/3; LL mR=r-mL+l; 49 LL tmp1=check(mL,L1),tmp2=check(mR,L2); 50 bool side=0; 51 if(tmp1==tmp2 && L1==L2){ 52 ans=max(ans,tmp1);l=mL+1;r=mR-1; 53 } 54 if(tmp1==tmp2) 55 side=(L1>=L2); 56 else side=(tmp1<tmp2); 57 if(side)l=mL+1; 58 else r=mR-1; 59 ans=max(ans,max(tmp1,tmp2)); 60 } 61 printf("%lld\n",ans); 62 return; 63 } 64 int main(){ 65 int i,j; 66 while(scanf("%lld%lld%d",&M,&F,&n)!=EOF){ 67 for(i=1;i<=n;i++){ 68 a[i].p=read(); a[i].s=read(); 69 } 70 cnt=0; 71 for(i=1;i<=n;i++){ 72 for(j=1;j<=n;j++) 73 if(i!=j && a[i].p>=a[j].p && a[i].s<=a[j].s)break; 74 if(j>n)a[++cnt]=a[i]; 75 } 76 n=cnt; 77 sort(a+1,a+n+1); 78 solve(); 79 } 80 return 0; 81 }
本文为博主原创文章,转载请注明出处。