题解 P3045 [USACO12FEB] Cow Coupons G
InfiniteHorizon · · 题解
这是反悔贪心的一道好题。
有人问,反悔贪心是什么?
其实贪心本身不带有反悔,是因为此时的贪心可以从局部最优解推出全局最优解。但当有些时候局部最优解推不出全局最优解时,就要用反悔贪心,在适当的时候撤销之前做出的决策。
我们先选取
当前考虑第
如果要反悔,那么用优惠券买
意思是:
发现
然后把下标相同的归在小于号的同一侧:
他们的形式是相同的,所以我们可以设
所以只要在已经使用优惠券的物品里面,存在一个
最小值可以用优先队列来求。
话不多说,上代码,代码中用
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=50010;
int n,k,m;
int p[maxn],c[maxn];
bool buy[maxn];//buy[i] 表示第 i 个物品是否被买过
int ans=0;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >P,C;
priority_queue<int,vector<int>,greater<int> >delta;
signed main(){
cin>>n>>k>>m;
for(int i=1;i<=n;++i){
cin>>p[i]>>c[i];
P.push(make_pair(p[i],i));
C.push(make_pair(c[i],i));
}
for(int i=1;i<=k;++i) delta.push(0);
while(!P.empty()){
pair<int,int> x1=P.top();
pair<int,int> x2=C.top();
if(buy[x1.second]){//如果被买过了,就跳过
P.pop();
continue;
}
if(buy[x2.second]){
C.pop();
continue;
}
if(delta.top() > x1.first-x2.first){//这个式子和上文推的式子时相反的,表示用原价买 i 更划算
m-=x1.first;
P.pop();
buy[x1.second]=true;
}
else{//否则的话,就是用优惠券买 i 更划算
m-=x2.first+delta.top();
delta.pop();
C.pop();
buy[x2.second]=true;
delta.push(p[x2.second]-c[x2.second]);
}
if(m>=0) ans++;
else break;
}
cout<<ans<<endl;
return 0;
}
最后,希望大家都能真正理解返回贪心,它的精髓就是“常思己过”!
静坐常思己过,闲谈莫论人非。——明 · 罗洪先《醒世诗》