洛谷P3045 [USACO12FEB]牛券Cow Coupons

题目:洛谷P3045 [USACO12FEB]牛券Cow Coupons

思路:

贪心

假设先用完所有优惠券,则只有两种决策:以原价购买未选物品中价格最低的;以最低代价退回当前用的一张优惠券,再以优惠价购买未选物品中优惠价最低的。

回退(反悔)操作可以用堆实现,具体实现:

用三个小根堆h1、h2、h3,分别存储未选物品的原价及编号、未选物品的优惠价及编号、已选物品退回优惠券的代价。

每次比较三个堆的堆顶t1、t2、t3,若当前花费加上min(t1,t2+t3)超过m就break,否则选择两种决策中花费较小的并弹掉堆顶。用bool数组记录每个物品是否被选过。

为了方便实现,最初可以在h3中加入k个0。


Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int> node;
const int N = 1e5 + 5;
priority_queue< node, vector<node>, greater<node> > h1, h2;
priority_queue< ll, vector<ll>, greater<ll> > h3;
ll n, k, m, cnt, sum, p[N], c[N];
bool mark[N];
inline void In(ll &num) {
	register char c = getchar();
	for(num = 0; !isdigit(c); c = getchar());
	for(; isdigit(c); num = num * 10 + c - 48, c = getchar());
}
int main() {
	In(n); In(k); In(m);
	for(int i = 1; i <= n; ++i) {
		In(p[i]); In(c[i]);
		h1.push(make_pair(p[i], i));
		h2.push(make_pair(c[i], i));
	}
	for(int i = 1; i <= k; ++i) h3.push(0ll);
	while(!h1.empty()) {
		node t1 = h1.top(), t2 = h2.top();
		ll t3 = h3.top();
		if(mark[t1.second]) {
			h1.pop();
			continue;
		}
		if(mark[t2.second]) {
			h2.pop();
			continue;
		}
		if(t1.first < t2.first + t3) {
			if(sum + t1.first > m) break;
			++cnt;
			h1.pop();
			sum += t1.first;
			mark[t1.second] = true;
		} else {
			if(sum + t2.first + t3 > m) break;
			++cnt;
			h2.pop();
			h3.pop();
			sum += t2.first + t3;
			mark[t2.second] = true;
			h3.push(p[t2.second] - c[t2.second]);
		}
	}
	printf("%lld\n", cnt);
	return 0;
}
posted @ 2019-09-14 17:59  宇興  阅读(189)  评论(0编辑  收藏  举报