题目:洛谷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; }