Bzoj2832 / Bzoj3874 宅男小C

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 124  Solved: 26

Description

众所周知,小C是个宅男,所以他的每天的食物要靠外卖来解决。小C现在有M元钱,他想知道这些钱他最多可以吃多少天。

 

餐厅提供N种食物,每种食物有两个属性,单价Pi和保质期Si,表示小C需要花Pi元才能买到足够一天吃的这种食物,并且需要在送到Si天内吃完,否则食物会变质,就不能吃了,若Si为0则意味着必须在送到当天吃完。另外,每次送餐需要额外F元送餐费。

 

Input

每个测试点包含多组测试数据;
每个测试数据第一行三个整数M,F,N,如题目描述中所述;
以下N行,每行两个整数,分别表示PiSi

Output

对于每个测试数据输出一行,表示最多可以吃的天数。

Sample Input

32 5 2
5 0
10 2
10 10 1
10 10
10 1 1
1 5

Sample Output

3
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 }

 

posted @ 2017-05-05 16:50  SilverNebula  阅读(207)  评论(0编辑  收藏  举报
AmazingCounters.com