题目:洛谷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
", cnt);
return 0;
}