【NOIP2017提高A组冲刺11.8】购物

这个范围对DP不友好,和CF的一道C题非常像,贪心+后悔。

先使用k个优惠券购买k个q最小的(钱不购买则退出),同时把这k个p[i]-q[i]放入小根堆,然后将剩下的n-k个按p升序排序,记小根堆堆顶为top,每次比较p[i]和q[i]+top(相当于反悔,用top的代价把之前的一个优惠券用到这里)

 感谢wwb的hack,虽然这题没出数据:当小根堆弹完之后就会出问题了,一个粗暴的解决方法是取堆顶之前判断是否空,若为空则返回极大值。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<queue>
#define int long long
using namespace std;

inline int rd(){
    int ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

priority_queue<int> Q;

inline void Push(int x){Q.push(-x);}
inline int Top(){return Q.empty()?1<<29:-Q.top();}

const int MAXN=50005;

int n,num,m;
int p[MAXN],q[MAXN];
int vis[MAXN];
int id[MAXN],iid[MAXN];
int ans=0,cnt=0;
bool cmp1(int x,int y){return q[x]<q[y];}
bool cmp2(int x,int y){return p[x]<p[y];}


signed main(){
    freopen("shopping.in","r",stdin);
    freopen("shopping.out","w",stdout);
    n=rd();num=rd();m=rd();
    for(int i=1;i<=n;i++){
        p[i]=rd();q[i]=rd();
        id[i]=iid[i]=i;
    }
    sort(id+1,id+1+n,cmp1);
    sort(iid+1,iid+1+n,cmp2);
    for(int i=1;i<=num;i++){
        if(ans+q[id[i]]>m) return cout<<cnt,0;
        ans+=q[id[i]];cnt++;
    }
    for(int i=1;i<=num;i++) Push(p[id[i]]-q[id[i]]),vis[id[i]]=1;
    for(int i=1;i<=n;i++){
        if(ans>m) return cout<<cnt-1,0;
        if(vis[iid[i]]) continue;
        if(q[iid[i]]+Top()>p[iid[i]]) {ans+=p[iid[i]],cnt++;continue;}
        ans+=q[iid[i]]+Top();cnt++;
        Q.pop();Push(p[iid[i]]-q[iid[i]]);
        
    }
    cout<<cnt-(ans>m);
    return 0;
}

 

转载于:https://www.cnblogs.com/ghostcai/p/9439274.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值