每日一题012-贪心和堆-洛谷p3045买奶牛

P3045 USACO12FEB Cow Coupons G

题目背景

Subtask 0 为原数据,Subtask 1,2 为 hack 数据。

题目描述

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 N N 头奶牛,第 i i i 头奶牛价格为 P i P_i Pi。FJ 有 K K K 张优惠券,使用优惠券购买第 i i i 头奶牛时价格会降为 C i C_i Ci,当然每头奶牛只能使用一次优惠券。FJ 想知道花不超过 M M M 的钱最多可以买多少奶牛?

  • 1 ≤ K ≤ N ≤ 5 × 1 0 4 1 \le K \le N \le 5 \times 10^4 1KN5×104
  • 1 ≤ C i ≤ P i ≤ 1 0 9 1 \le C_i \le P_i \le 10^9 1CiPi109
  • 1 ≤ M ≤ 1 0 14 1 \le M \le 10^{14} 1M1014

输入格式

* 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.

输入输出样例 #1

输入 #1

4 1 7 
3 2 
2 2 
8 1 
4 3

输出 #1

3

说明/提示

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.

思路

有什么问题可以在评论区问我,我会回复的

基础贪心

每次都买用优惠券价格最小的牛,优惠券用完之后就买原价最小的牛。看起来很好想到的贪心,然而只能过原数据。

解释一下为什么不行,看这个样例

2 1 5
2 1
1000 3

如果按照优惠券先排序,就会把优惠券用在第一头牛上面,然后剩下的钱就不能买下1000块钱的原价牛。最终只能买一头牛,但是把优惠券用在第二头牛上面,就可以买两头牛了。

后悔贪心

依然是先把优惠券用完,然后开始选综合价格最便宜的买。我们有两种选择,一种是按照原价买这头牛,另一种是从之前的换出一张优惠券给他,之间某头牛不用优惠券了。那么哪个更好呢,我们可以算一下

设这头牛是j,之前用优惠券买的牛是i。换出优惠券购买新牛需要花的钱是 C [ j ] + P [ i ] − C [ i ] C[j]+P[i]-C[i] C[j]+P[i]C[i],买原价最小的新牛花的钱是 P m i n P_{min} Pmin,。
我们想让这个综合价钱最小,那么新牛的 C [ j ] C[j] C[j]就要小,之前的牛的 P [ i ] − C [ i ] P[i]-C[i] P[i]C[i]也要小。所以我们用优先队列维护最小的C的牛,以及用优先队列维护用优惠券购买的最小的 P [ i ] − C [ i ] P[i]-C[i] P[i]C[i]的差值。最后用一个优先队列维护原价P最小的牛。

得到了换优惠券方式买的最小价钱和用原价方式买的最小价钱(这两头牛可以是不同的牛牛)然后比较即可。在此期间钱不够了就退出。

总之我仍然是永远只挑最小价钱买,但是后面最小价钱可以使用一个换算过后的综合价钱来和原价比较。

代码

#include 
#include 
#include 
#define PII std::pair<i64, i64>  //使用pair记录编号
using i64 = long long;

i64 n, k, m;
i64 cur;  //记录目前花的钱
i64 ans;  //记录买的牛的头数
bool bt[50004];
int p[50004];
int c[50004];
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q1;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q2;
std::priority_queue<i64, std::vector<i64>, std::greater<i64>> q3;
int main()
{
	std::cin>>n>>k>>m;
	for(int i = 1; i<=n; i++)
	{
		std::cin>>p[i]>>c[i];
		q1.push(std::make_pair(p[i],i));
		q2.push(std::make_pair(c[i],i));
	}
	for(int i = 1; i<=k; i++)
	{
		q3.push(0);    //k张券,当我们要使用的时候,如果还有优惠券没用就是0,会优先弹出
	}
	while(!q1.empty()&&!q2.empty())
	{
		if(!q1.empty()&&bt[q1.top().second])
		{
			q1.pop(); continue;  //如果该牛被买,跳过
		}
		if(!q2.empty()&&bt[q2.top().second])
		{
			q2.pop();continue;   //如果该牛被买,跳过
		}
		PII x1 = q1.top(); PII x2 = q2.top(); 
		if(x2.first+q3.top()<x1.first)  //x1.first就是Pmin
		{
			if(cur+x2.first+q3.top()>m) //钱不够退出
			{
				std::cout<<ans; return 0;
			}
			ans++; cur+=x2.first+q3.top(); bt[x2.second] = 1;
			q3.pop(); q3.push(p[x2.second]-x2.first);
		}
		else{
			if(cur+x1.first>m)  //钱不够退出
			{
				std::cout<<ans; return 0;
			}
			cur+=x1.first; ans++; q1.pop(); bt[x1.second] = 1;
		}
	}
	std::cout<<ans;  //钱够,输出
	return 0;
}

你可能感兴趣的:(每日一题,算法,java,数据结构,c++,蓝桥杯,贪心算法,堆)