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

猜你喜欢

转载自www.cnblogs.com/yu-xing/p/11519717.html