Description
很久很久很久以前的一道题,当时真的不会做。
我也不知道当时他们怎么A的。当时好像没学反悔型贪心啊。。。
最近被贪心虐的不轻,又除了一个变种:反悔型的。
需要稍微写一些了。啊啊啊教练我真的没有在水题啊我真不会
认真的讲,这题不错,很经典的反悔
反悔型贪心的特点就是当前获得的收益到时候可以再返还来得到更长远的收益,这就克服了贪心的目光短浅的弊端。
在这道题里,我们反悔的主要原因就是优惠券的应用。
最开始我们买下k头用了优惠券后最便宜的牛,这是初步贪心。
如果我们连这k头都买不起的话直接跳出就好了。
但是这样做的话我们用优惠券所省下的钱可能不是最多的
具体怎么反悔呢?其实我们的反悔就是要改变对优惠券的使用。
那么我们就花钱把优惠券买回来呗!
我们在买前k头牛的时候,把p-c加入一个小根堆,表示你要花多少钱才能再得到一张优惠券。
再开两个堆,把其它的牛存进去,分别存p和c。
然后不断采取最优决策:买回一张券再买一头折扣后的牛,或者不折扣地买一头牛。
不断取最便宜的。这样的话我们的贪心策略就正确了。
要注意为了防止牛买重了,所以c和p两个堆都要把牛的编号记下来,弹出时打上标记表示被买过。
这样就不会让一头牛被用券买了一次又没用券买了一次从而产生2的贡献。
要注意所有的牛都买完之后及时跳出。。。
细节还是挺多的。当时没A就对了。
好题。
1 #include<cstdio> 2 #include<queue> 3 #include<algorithm> 4 using namespace std; 5 priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >cow,discounted_cow; 6 priority_queue<int,vector<int>,greater<int> >coupon; 7 int n,k,p[50005],c[50005];pair<int,int>r[50005];long long m;char al[50005]; 8 int main(){ 9 scanf("%d%d%lld",&n,&k,&m); 10 for(int i=1;i<=n;++i)scanf("%d%d",&p[i],&c[i]),r[i]=make_pair(c[i],i); 11 sort(r+1,r+1+n); 12 for(int i=1;i<=k;++i){ 13 if(m<r[i].first){printf("%d\n",i-1);return 0;} 14 m-=r[i].first;coupon.push(p[r[i].second]-r[i].first);al[r[i].second]=1; 15 } 16 for(int i=1;i<=n;++i)if(!al[i]) 17 cow.push(make_pair(p[i],i)),discounted_cow.push(make_pair(c[i],i)); 18 int ans=k; 19 while(ans<n){ 20 if(coupon.top()+discounted_cow.top().first<cow.top().first){ 21 if(coupon.top()+discounted_cow.top().first>m){printf("%d\n",ans);return 0;} 22 m-=coupon.top()+discounted_cow.top().first; 23 int ord=discounted_cow.top().second; 24 al[ord]=1;coupon.pop();coupon.push(p[ord]-c[ord]); 25 ans++; 26 }else{ 27 if(cow.top().first>m){printf("%d\n",ans);return 0;} 28 m-=cow.top().first;al[cow.top().second]=1;ans++; 29 } 30 while(!discounted_cow.empty()&&al[discounted_cow.top().second])discounted_cow.pop(); 31 while(!cow.empty()&&al[cow.top().second])cow.pop(); 32 }printf("%d\n",n); 33 }