1. 程式人生 > 其它 >題解 P3045 【[USACO12FEB]牛券Cow Coupons】

題解 P3045 【[USACO12FEB]牛券Cow Coupons】

題目傳送門

Desprition

\(FJ\) 準備買一些新奶牛,市場上有 \(N\) 頭奶牛、\(K\) 張優惠券,優惠劵可降價,每頭奶牛隻能使用一次優惠券。問知道花不超過 \(M\) 的錢最多可以買多少奶牛?

Solution

貪心 + 優先佇列

首先,根據經驗, \(k\) 張優惠券肯定是儘量全用的…… 不要白不要嘛

優惠券用不完的情況很好處理,下面只對錢有剩餘還可以再買的情況進行分析。

可以得到幾個條件(具體證明參見暮光閃閃的證明)

  • 優惠後價格最低的前 \(k\) 頭牛一定在購買序列中;
  • 優惠券不一定會用在這前 \(k\) 頭牛中;

所以當前需要研究的是怎樣用剩餘的錢買儘可能多餘下的牛。(注意後文中牛的順序

是 按\(c_i\) 從小到大排序後的順序)

對於一頭牛 \(i\) ,有兩種情況:

  • 不用券, 則多花費 \(p_i\) 元;
  • 用券,則多花費 \(c_i\) 元,但是現在假設的是 \(k\) 張券全部用在前 \(k\) 頭牛,所以需要在用券前 \(k\) 中找出牛 \(j\),將券騰出來,那實際增加的花費應該為 \(c_i + p_j - c_j\) ;

又因為想買更多的牛,所以不管 \(p_i\) 還是 \(c_i\) 或者 \(c_i + p_j - c_j\),都要求花費最少。

因此用三個優先佇列維護即可。

Code


#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define ll long long
const int N = 5e4 + 5;
struct node {
	int p,c,id;
}cow[N];
struct cmp {
	bool operator () (const node x,const node y) {return x.c > y.c;}	
};
struct cmp2 {
	bool operator () (const node x,const node y) {return x.p > y.p;}	
};
struct cmp3 {
	bool operator () (const node x,const node y) {return x.p - x.c > y.p - y.c;}	
};
priority_queue<node, vector<node>, cmp> q;
priority_queue<node, vector<node>, cmp2> q2;
priority_queue<node, vector<node>, cmp3> q3;
int n,k,ans;
ll m;
bool used[N];
int main() {
	scanf("%d %d %lld",&n,&k,&m);
	for(int i = 1; i <= n; i ++) {
		scanf("%d %d",&cow[i].p,&cow[i].c);
		cow[i].id = i, q.push(cow[i]), q2.push(cow[i]);
	}
	for(int i = 1; i <= min(n,k); i ++) {
		m -= q.top().c;
		if(m < 0) {printf("%d\n",ans); return 0;}
		ans ++, used[q.top().id] = 1;
		q3.push(q.top()), q.pop();
	}
	node x, y, z;
	for(int i = k + 1; i <= n; i ++) {
		while(used[q2.top().id]) q2.pop();
		while(used[q.top().id]) q.pop();
		x = q.top(), y = q2.top(), z = q3.top();
		if(y.p < x.c + z.p - z.c) m -= y.p, q3.push(y), used[y.id] = 1, q2.pop();
		else m -= x.c + z.p - z.c, used[x.id] = 1, q.pop(), q3.pop();
		if(m < 0) break;
		ans ++;
	}
	printf("%d",ans);
	return 0;
}