奶茶代金券问题C++贪心算法

考虑到两个大于 的奶茶组合起来和单独购买浪费的钱数是一样的(虽然本题中无法单独购买奶
茶),想要省钱,那么应该考虑组合两个小于 的奶茶,或者组合一个大于 的(下文简称“大
的”),一个小于 的(下文简称“小的”)。
贪心思路应该是优先将大的和小的匹配进行购买,如果匹配结束后还有多余的“小的”,那么将“小的”之间两两匹配;如果匹配结束之后还有多余的“大的”,那么无法继续匹配。
考虑大的和小的进行匹配的过程,恰好小于 的奶茶只能和恰好大于 的奶茶匹配。例如
的时候,4 只能和 6 匹配,如果和 7、8、9 匹配,则浪费的钱数和单独购买相同,导致无意义贪心。而价格为 1 的奶茶可以和 6、7、8、9 匹配,我们可以感觉到,所有小于 的奶茶中,越接近 的奶茶匹配的选择越少,所以应该被优先匹配。
最终思路:将所有“小的”从大到小排序,然后开始匹配,匹配的时候将所有“大的”从小到大进行匹配,类似双指针维护匹配过程。最终再判断是否还有多余的“小的”,将这些“小的”两两匹配。(随便怎么匹配都是一样的)

#include <iostream>
#include <set>
using namespace std;
#define ll long long
const int maxn = 1e5;
set<pair<int, int> > s;
int main(){
    ll n, m, a, b, ans = 0;
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> a >> b;
        b %= m;
        if(b == 0)    continue;
        b = m - b;
        ans += b * a;
        s.insert(make_pair(b, a));
    }
    auto r = s.lower_bound(make_pair(m/2, 0)), l = r;
    l--;
    ll rcnt = (*r).second, lcnt = 0;
    if(r != s.begin()){
        lcnt = (*l).second;
    }else{
        ll allcnt = 0;
        while(r != s.end()){
            allcnt += rcnt;
            r++;
            rcnt = (*r).second;
        }
        ans -= allcnt / 2 * m;
        cout << ans << endl;
        return 0;
    }
    ll costsum = 0;
    if((*r).first == m / 2){
        r++;
        rcnt = 0;
        if(r != s.end())
            rcnt = (*r).second;
    }    
    while(true){
        if(!rcnt){
            r++;
            if(r == s.end())    break;
            rcnt = (*r).second;
        }
        if(!lcnt){
            if(l == s.begin())    break;
            l--;
            lcnt = (*l).second;
        }
        while((*l).first + (*r).first < m && r != s.end()){
            r++;
            rcnt = (*r).second;
        }
        if(r == s.end())    break;
        ll now = min(lcnt, rcnt);
        lcnt -= now; rcnt -= now;
        costsum += now;
        ans -= now * m;
    }
    ll allcnt = 0;
    r = s.lower_bound(make_pair(m/2, 0));
    while(r != s.end()){
        allcnt += (*r).second;
        r++;
        rcnt = (*r).second;
    }
    ans -= (allcnt - costsum) / 2 * m;
    cout << ans << endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

!chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值