题目描述
贩卖机只支持硬币支付,且收退都只支持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+(y−1))/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;
}
};