贪心算法进阶(Luogu P2376)

Luogu P2376

题目传送门

题目想要求出能够支付零花钱的最大星期数,而每种硬币的面额和数量都是固定的,那么就要使浪费尽可能少,所以贪心应该是这道题的正确思路。

贪心策略

  1. 在输入时判断硬币面额
    • 如果面额大于每星期需支付的最小零花钱,那么就说明一枚这种硬币就可以独立支付一个星期的零花钱,就不需要和其他硬币共同支付。这样的话,要使浪费尽可能少,每次就应该只支付一枚这种硬币。所以能支付的星期数 + 这种硬币的数量
    • 如果面额小于等于每星期需支付的最小零花钱,那么就说明一枚这种硬币无法独立支付一个星期的零花钱,因此需要和其他硬币一起支付。我们先将这种硬币的面额和数量存进一个结构体,后续进行判断。
  2. 对于每一个星期
    • 因为当前剩余的所有硬币面额都小于最小零花钱,因此不存在独立支付的情况。
    • 当不一定会产生浪费时:因为小面额硬币能够整除所有比它大的面额,所以小面额可以凑出大面额。因此为了避免后续不必要的浪费,我们优先使用大面额硬币。
    • 当浪费无可避免时(即本周空缺小于所有面额时):为了使浪费最小,我们优先使用小面额。
    • 如果无论如何都无法凑出大于等于最小零花钱的面额,就说明手中剩下的钱无法再支付一个星期的零花钱,结束贪心。

AC代码(含注释)

代码中有防 copy hh,相信认真看的同学们都看出来了。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
int n, least_num;//面额数,每星期最少零花钱数目
int ans, cnt;

struct money
{
    int value;//面额
    int num;//数量
}coin[21];

bool cmp(money a, money b){
    return a.value > b.value;
}

int main(){

    freopen("code.in", "r", stdin);
    freopen("code.out", "w", stdout);

    cin >> n >> least_num;
    for(int i = 1; i <= n; i++){
        int a, b;
        cin >> a >> b;
        if(a >= least_num){//面额大于最小零花钱的硬币,可以独立支付一个星期的零花钱
            ans += b;
            continue;
        }
        else {//面额小于最小零花钱的硬币需要和其他硬币一起支付,所以先存起来后续再判断
            coin[++cnt].value = a;
            coin[cnt].num = b;
        }
    }
    sort(coin + 1, coin + 1 + cnt, cmp);//降序排列

    while(1){//无限循环,等到不足以支付了就主动跳出
        int k = least_num;
        for(int i = 1; i <= cnt; i++){//先用大面额
            //当仍需要新的硬币才能支付本周零花钱时
            while(k > 0 && coin[i].num && k >= coin[i].value){
                k -= coin[i].value;
                --coin[i].num;
            }
        }
        for(int i = cnt; i >= 1; i--){//如果一定会产生浪费,那就用尽量小的面额
            if(k > 0 && coin[i].num && coin[i].value >= k){
                --coin[i].num;
                k -= coin[i].value;
            }
        }
        if(k > 0)break;//如果无法凑出大于等于k的值,就说明手中剩下的钱无法再支付一个星期的零花钱
        else ans++;//可以凑出k的值,这个星期正常支付
    }

    cout << ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值