zoukankan      html  css  js  c++  java
  • bzoj 3874: [Ahoi2014]宅男计划

    [AHOI2014/JSOI2014]宅男计划:

    $ solution: $

    看到这一题题面,莫名想到了(蔬菜),于是莫名开始恐慌。考场上只知道有个贪心计算快递小哥来一次,我要买能活n天的最小花费,却没想到还有一个三分法来枚举快递小哥来的次数!

    首先我们可以脑补一下,快递小哥来的次数,和宅男活的总天数是成一个二次函数关系的。就像快递小哥来的次数少,那宅男多数的钱只能分到这么少的购买次数中,因为便宜的保质期短,每次必然会买一些保质期长价格贵的食物;当快递小哥来的次数多了,那我的钱可以分配到很多次购买机会中,是可以每次买些便宜的,可是你就会发现钱不多了(都给快递小哥付运费了!)所以我们三分找到中间的那一个平衡点(合理的购买次数能(钱尽其用)活得更久!!)

    这是三分,那本题贪心贪在哪儿呢?我们读题发现,他给你的食品中肯定有一些垃圾食品(价格贵,保质期又短),我们可以进行筛选:单调队列,先将食品按保质期从大到小排序,然后放入以价格单调增的队列中去(因为后面放进取得食品保质期一定更长(排了序的),一但价格还比前一个低,就可以取代前一个食品)

    然后还有一个贪心,就是在 $ check() $ 函数中,计算能活的最长天数时,只要在保质期内,我一定买最便宜的。就像我现在经筛选后有两个食品,一个保质期为5,价格为4,另一个保质期为8,价格为7,那我 $ [1,6] $ 天一定买第一个, $ [7,9] $ 天一定买第二个!因为我们单调栈中时间从小到大,所以每次算活得最长天数时复杂度 $ O(n)

    $ 再加上三分的复杂度,本题刚好够用!

    然后对代吗做个解释:因为根据题意,保质期为1天的食品,可以留两天!!!(被这个坑惨了)所以我们在读入时干脆就给保质期加一,这样方便运算!然后解释一下我的 $ check() $ 函数:

    1. 我的 $ check() $ 买东西时,是同步的,如果买一个食品,那快递小哥来的 $ x $ 次都买这个食品(那个 $ if $ 除外)
    2. $ left: $ 我还剩下的钱的数量,(因为特性1,所以每一次都要减去我买的食品的价格乘以它的数量再乘以 $ x $ 天)
    3. $ now: $ 我同步买东西能同步维持的天数目前是多少
    4. $ day: $ 我有几天需要买这一种食品(如果没有足够的钱贪心的买,就会转到 $ if $ 中去)
    5. $ tot: $ 我总共能维持多少天
    6. $ if(now<b[i].t): $ 我没足够的钱来 $ x $ 天同步买一个东西,那我能买几天买几天,然后 $ return $

    接下来一切以代码为准:

    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define inf 0x7fffffff
    #define rg register int
    
    using namespace std;
    
    struct su{
    	ll v,t;
    }a[215],b[215];
    
    ll n,top;
    ll ans,m,f;//如题
    
    inline ll qr(){//快读,可以忽略
    	char ch;
    	while((ch=getchar())<'0'||ch>'9');
    	ll res=ch^48;
    	while((ch=getchar())>='0'&&ch<='9')
    		res=res*10+(ch^48);
    	return res;
    }
    
    inline bool cmp(su x,su y){return x.t<y.t;}
    
    inline ll check(ll x){
    	ll left=m-x*f,now=0,day,tot=0;//剩余的钱,每一次买的食物能维持几天,我还有几天需要买这个食物,总共能维持多少天
    	if(left<0)return 0;
    	for(rg i=1;i<=top;++i){//快递小哥来的x次都买这种食物
    		day=min(b[i].t-now,left/(b[i].v*x));//每一次都买几个
    		now+=day; tot+=day*x; left-=b[i].v*day*x;//进行统计转移
    		if(now<b[i].t){tot+=left/b[i].v;break;}//剩余的钱不能支持每一次都买了,就能卖几次买几次,然后return
    	}return tot;
    }
    
    int main(){
    	//freopen("food.in","r",stdin);
    	//freopen("food.out","w",stdout);
    	m=qr();f=qr();n=qr();
    	for(rg i=1;i<=n;++i)
    		a[i]=su{qr(),qr()+1};
    	sort(a+1,a+n+1,cmp); //按时间排序!!!(非价值)
    	for(rg i=1;i<=n;b[++top]=a[i],++i)
    		while(top&&a[i].v<=b[top].v)--top;//用单调栈进行食品筛选(加速)
    	ll l=1,r=m/(f+b[1].v),mid1,mid2,tot,s1,s2;//r的值可以算出来
    	while(l<=r){ //三分法求峰值
    		tot=r-l+1,mid1=l+tot/3,mid2=l+tot*2/3;
    		s1=check(mid1);s2=check(mid2);
    		ans=max(ans,(s1<s2?l=mid1+1,s2:r=mid2-1,s1));//究极的三目运算压行
    	}printf("%lld
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    asp的多国语言构思
    制作IE和FF都支持的无限级关联菜单
    破解网络尖兵(真正对付限制ADSL路由共享的方法)
    Asp透过系统DSN链接mysql数据库
    找到了一首曾经很喜欢的老歌
    生意人应具备的性格
    简单的操作让你的迅雷变的清爽
    线路分流自动跳转代码
    通过regsvr32注册DLL可以解决的一些疑难杂症
    页面无刷新超时自动退出
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/10332544.html
Copyright © 2011-2022 走看看