G: Cow Coupons

在这里插入图片描述
官方题解:

There are several different ways to approach this problem.
One of them stems from the initial idea of picking the lowest-cost cow each time: use all coupons on the cheapest
cows, then buy as many cows as possible without coupons. However, this doesn’t quite work: if several cows are very cheap with or without a coupon, and other cows are cheap with a coupon but very expensive without one, we can intuitively see that we would like to use our coupons on the more expensive cows. This leads to the idea of “revoking” a coupon: for cow i, we can pay (Pi−Ci) in order to regain one of our coupons (because we are now buying cow i at the “expensive” price).
After purchasing as many cows as possible with coupons, we store their (Pi−Ci) values in a heap.
To purchase a remaining cow j, we can either pay Pj or Cj+ (Pi−Ci), where cow i is the top cow in our heap.
This ensures we are always using exactly as many coupons as we can.
For each cow we add to our lineup, we are greedily paying the minimum possible amount for it, so this solution is clearly optimal.

题解大意:
先选出k头按c花费最小的牛,将这k头牛节省下的钱依次存入小根堆st。对于其他牛j,买牛j的最小花费 = min (Pj,(Pi-Ci)+ Cj) ,st实际就是在维护(Pi-Ci)组成的序列, 很明显(Pi-Ci)取st的堆顶时最小。由此得出买牛j的最小花费= min(Pj,st.top()+Cj).

因此还需要维护Pj组成的一个堆,Cj组成的一个堆。每次都选择两种堆中更优的堆顶。用multiset实现堆,操作和优先队列类似。

code:

#include <bits/stdc++.h>
#define int long long
using namespace std;

struct Node{
    int val , id ;
    bool operator < (Node no1) const{
        if(val!=no1.val) return val < no1.val ;
        else return id < no1.id ;
    }
};

multiset<Node> ch , ep;
multiset<int> st;
int n , m , k ;
bool vis[50005] ;
int p[50005], c[50005] ;
signed main()
{
    //freopen("in","r",stdin);
    scanf("%lld%lld%lld",&n,&k,&m);
    for(int i = 0 ; i < k ; i++){
        st.insert(0);
    }
    for(int i = 0 ; i < n ; i++) {
        scanf("%lld%lld",&p[i],&c[i]);
        ch.insert({c[i],i});
        ep.insert({p[i],i});
    }
    int ans = 0 ;
    while(ans < n && m > 0){
        while(!ch.empty()&&vis[(*ch.begin()).id])ch.erase(ch.begin());
        while(!ep.empty()&&vis[(*ep.begin()).id])ep.erase(ep.begin());
        Node C = *ch.begin() , P = *ep.begin();
        if(C.val + *st.begin() < P.val){
            m = m - (C.val + *st.begin()) ;
            vis[C.id] = true;
            ch.erase(ch.begin());
            st.erase(st.begin());
            st.insert(p[C.id] - c[C.id]);
        }else{
            m = m - P.val ;
            vis[P.id] = true;
            ep.erase(ep.begin());
        }
        if(m>=0) ans++;
    }
    cout << ans << endl ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值