算法:买饮料

题目描述

贩卖机只支持硬币支付,且收退都只支持10 ,50,100三种面额

一次购买只能出一瓶可乐,且投钱和找零都遵循优先使用大钱的原则,需要购买的可乐数量是m,

其中手头拥有的10、50、100的数量分别为a、b、c
可乐的价格是x(x是10的倍数)

请计算出需要投入硬币次数?

题目解析

这种题目在面试场上出现的时候,就考你coding能不能把模拟行为加速

大流程

关注一个面值最大能搞定多少瓶可乐

  • 我们先看耗尽100面值,能搞定多少瓶可乐,然后耗尽它之后,找到的钱分到后面的面值去
  • 我们再看耗尽20面值,能搞定多少瓶可乐,然后耗尽它之后,找到的钱分到后面的面值去
  • 一旦可乐的数量大于等于m,投币数量就出来了

举个例子,如下:

  • 我们先考虑100张的面值
    在这里插入图片描述

  • 现在我们考虑50张的面值,

    • 当买第一瓶的时候,需要先耗尽历史记录
      • 1张100 + 3张50 = 250
    • 然后再去看单独的50,能搞定多少可乐
      在这里插入图片描述
  • 注意:历史没有必要知道细节,只要知道总面值和张数就可以了

在这里插入图片描述

问题:可乐单价x,一共有y元,最多能买几瓶可乐?

  • 5,10 — > 10 / 5 = 2瓶
  • 5,11 ----> 11 / 5 = 2瓶
  • 5,9 ----> 9 / 5 = 1 瓶
  • ping = y / x

问题:当前有花x元,你有y元的硬币,你至少需要多少y才能买下这个东西?

  • x / y 向上取整
    • 如果没有这么多个硬币,那么就买不起
    • 如果多了就找钱
  • 怎么向上取值呢? ( x + ( y − 1 ) ) / y (x + (y - 1)) / y (x+(y1))/y

实现:

class  Solution{
    // 找钱 
    // conis ,zhang,i,买一次要找多少钱,一共买几次
    void giveRest( std::vector<int> &coins, std::vector<int> &zhang, int i, int oneTimeRest, int times){
        for (; i < 3; i++) {
            zhang[i] += (oneTimeRest / coins[i]) * times;
            oneTimeRest %= coins[i];
        }
    }
public:

    // 要买的可乐数量,m
    // 100元有a张
    // 50元有b张
    // 10元有c张
    // 可乐单价x
    int buyCnt(int m, int a, int b, int c, int x){
        std::vector<int> coins {100, 50, 10};
        std::vector<int> zhang {c, b, a};

        int puts  = 0;  // 总共需要多少次投币
        int preCoinsRest = 0; // 之前面值的钱还剩下多少总钱数
        int preCoinsZhang = 0; // 之前面值的钱还剩下多少总张数
        for (int i = 0; i <3 & m != 0; ++i) {
            // 要用之前剩下的钱、当前面值的钱,共同买第一瓶可乐
            // 那么当前面值参与搞定第一瓶可乐,需要掏出多少张呢?
            int currleastZhangBuyFirst = (x - preCoinsRest + (coins[i] - 1)) / coins[i];
            if(zhang[i] >= currleastZhangBuyFirst){ // 如果之前的钱和当前面值的钱,能凑出第一瓶可乐
                // 凑出来了一瓶可乐也可能存在找钱的情况
                giveRest(coins, zhang, i + 1, (preCoinsRest + coins[i] * currleastZhangBuyFirst) - x, 1);
                puts += currleastZhangBuyFirst + preCoinsZhang;
                zhang[i] -= currleastZhangBuyFirst;
                m--;
            }else{  // 如果之前的钱和当前面值的钱,不能凑出第一瓶可乐
                preCoinsRest += coins[i] * zhang[i];
                preCoinsZhang += zhang[i];
                continue;
            }
            // 凑出第一瓶可乐之后,当前的面值有可能能继续买更多的可乐
            // 以下过程就是后续的可乐怎么用当前面值的钱来买
            // 用当前面值的钱,买一瓶可乐需要几张
            int curBuyOneColaZhang = (x + coins[i] - 1) / coins[i];
            // 用当前面值的钱,一共可以搞定几瓶可乐
            int curBuyColas = std::min(zhang[i] / curBuyOneColaZhang, m);
            // 用当前面值的钱,每搞定一瓶可乐,收货机会吐出多少零钱
            int oneTimeRest = coins[i] * curBuyOneColaZhang - x;
            // 找钱(零钱去提升后面几种面值的硬币数)
            giveRest(coins, zhang, i + 1, oneTimeRest, curBuyColas);
            // 当前面值去搞定可乐这件事,一共投了几次币
            puts += curBuyOneColaZhang * curBuyColas;
            // 还剩下多少瓶可乐需要去搞定
            m -= curBuyColas;
			// 当前剩下的作为历史剩下的作为历史
            zhang[i] -= curBuyOneColaZhang * curBuyColas;  //剩下张数:一共多少张 -  用了几张(买一次多少张  * 一共多少次)
            preCoinsRest = coins[i] * zhang[i]; //剩下多少钱: 当前面值 * 剩下张数
            preCoinsZhang = zhang[i];  	// 剩下张数
        }

        return m == 0 ? puts : -1;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值