[USACO12FEB] 牛券Cow Coupons
来了来了,贪心来了。话说写不出来题就去贪心啦~
题目描述
Farmer John needs new cows! There are N cows for sale (1 <= N <= 50,000), and FJ has to spend no more than his budget of M units of money (1 <= M <= 10^14). Cow i costs P_i money (1 <= P_i <= 10^9), but FJ has K coupons (1 <= K <= N), and when he uses a coupon on cow i, the cow costs C_i instead (1 <= C_i <= P_i). FJ can only use one coupon per cow, of course.
What is the maximum number of cows FJ can afford?
FJ准备买一些新奶牛,市场上有N头奶牛(1<=N<=50000),第i头奶牛价格为Pi(1<=Pi<=10^9)。FJ有K张优惠券,使用优惠券购买第i头奶牛时价格会降为Ci(1<=Ci<=Pi),每头奶牛只能使用一次优惠券。FJ想知道花不超过M(1<=M<=10^14)的钱最多可以买多少奶牛?
输入输出格式
输入格式:
* Line 1: Three space-separated integers: N, K, and M.
* Lines 2..N+1: Line i+1 contains two integers: P_i and C_i.
输出格式:
* Line 1: A single integer, the maximum number of cows FJ can afford.
说明
FJ has 4 cows, 1 coupon, and a budget of 7.
FJ uses the coupon on cow 3 and buys cows 1, 2, and 3, for a total cost of 3 + 2 + 1 = 6.
背景
当我开始写题解的时候,绝对是我DP写挂了,来换心情的。。。。
大概是前天的时候,学长讲了这道题,昨天写了进阶版的题解。
有人跟我说不太懂可反悔的贪心是什么,所以我就来整理一下。
可反悔的贪心
贪心大家都了解吧,就是选择当前的最优策略,整合后形成最优解。
但是这个时候我们可以发现一个问题:
如果局部最优解与形成整体最优解的情况矛盾怎么办?
举个例子具体理解一下,
a有两种选择x1,x2;b有两种选择,y1,y2
假设,让a满足最优的解法是选择x1,但是使得整体最远的解法则是在a处选择x2.
那么我们怎么样才能使得这种策略能够被选到呢?
搜索算法自然是可以做到的,但是一旦数据范围变大就炸掉了。
那么究竟该怎么办呢?
我们来仔细思考一下,阻挡我们使用贪心的最大问题是什么?
是不是一旦局部最优解不是整体最优解,就完全错误?
那么假设我们可以时光回溯,回到错误的点修改答案是不是就正确了呢?
好的,那么只要我们记录一下当前策略与未来策略的差值,当出现问题的时候,直接修改就ok了。
关于实现问题,就是使用堆栈处理,一般是大根堆。
这就是可修改的贪心。
思路分析
在这道题,我们可以发现,对于每一头牛来说,其实都有两个状态。
那么我们就可以把两个状态当作是当前可以选择的策略,用优惠券和不用优惠券。
接下来跑一次可反悔的贪心就ok。
然后我们就会发现一个问题, 优惠券是有数量的。
这个也好处理,我们优先处理使用优惠券之后最便宜的几头牛,然后选择剩下的牛中不用券最便宜的。
之后判断要不要将用过的一张券转用给一头新的牛。
思路就是开一个大根堆,然后依次push进去p[i] - c[i]
取出使用即可。
代码实现
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #define ll long long using namespace std; int n,k; ll m; struct ss{ ll p,c; }cow[50001]; priority_queue<long long,vector<long long>,greater<long long> > q; int cmp(ss a,ss b) { return a.c<b.c; } int cmmp(ss a,ss b) { return a.p<b.p; } int main() { scanf("%d%d%lld",&n,&k,&m); for(int i=1;i<=n;i++) scanf("%lld%lld",&cow[i].p,&cow[i].c); sort(cow+1,cow+n+1,cmp); ll sum=0; for(int i=1;i<=k;i++) { sum+=cow[i].c; if(sum>m) { cout<<i-1<<endl; return 0; } q.push(cow[i].p-cow[i].c); } sort(cow+k+1,cow+n+1,cmmp); for(int i=k+1;i<=n;i++) { if(cow[i].p-cow[i].c>q.top()) { sum+=q.top(); q.pop(); q.push(cow[i].p-cow[i].c); sum+=cow[i].c; } else sum+=cow[i].p; if(sum>m) { cout<<i-1<<endl; return 0; } } cout<<n<<endl; return 0; }