这个范围对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; }