leetcode-master/problems/0860.柠檬水找零.md

264 lines
8.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 860.柠檬水找零
[力扣题目链接](https://leetcode-cn.com/problems/lemonade-change/)
在柠檬水摊上每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零返回 true 否则返回 false 
示例 1
输入:[5,5,5,10,20]
输出true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。
示例 2
输入:[5,5,10]
输出true
示例 3
输入:[10,10]
输出false
示例 4
输入:[5,5,10,10,20]
输出false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。
提示:
* 0 <= bills.length <= 10000
* bills[i] 不是 5 就是 10 或是 20 
## 思路
这是前几天的leetcode每日一题感觉不错给大家讲一下。
这道题目刚一看,可能会有点懵,这要怎么找零才能保证完整全部账单的找零呢?
**但仔细一琢磨就会发现,可供我们做判断的空间非常少!**
只需要维护三种金额的数量510和20。
有如下三种情况:
* 情况一账单是5直接收下。
* 情况二账单是10消耗一个5增加一个10
* 情况三账单是20优先消耗一个10和一个5如果不够再消耗三个5
此时大家就发现 情况一,情况二,都是固定策略,都不用我们来做分析了,而唯一不确定的其实在情况三。
而情况三逻辑也不复杂甚至感觉纯模拟就可以了,其实情况三这里是有贪心的。
账单是20的情况为什么要优先消耗一个10和一个5呢
**因为美元10只能给账单20找零而美元5可以给账单10和账单20找零美元5更万能**
所以局部最优遇到账单20优先消耗美元10完成本次找零。全局最优完成全部账单的找零。
局部最优可以推出全局最优,并找不出反例,那么就试试贪心算法!
C++代码如下:
```CPP
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0, twenty = 0;
for (int bill : bills) {
// 情况一
if (bill == 5) five++;
// 情况二
if (bill == 10) {
if (five <= 0) return false;
ten++;
five--;
}
// 情况三
if (bill == 20) {
// 优先消耗10美元因为5美元的找零用处更大能多留着就多留着
if (five > 0 && ten > 0) {
five--;
ten--;
twenty++; // 其实这行代码可以删了因为记录20已经没有意义了不会用20来找零
} else if (five >= 3) {
five -= 3;
twenty++; // 同理,这行代码也可以删了
} else return false;
}
}
return true;
}
};
```
## 总结
咋眼一看好像很复杂,分析清楚之后,会发现逻辑其实非常固定。
这道题目可以告诉大家,遇到感觉没有思路的题目,可以静下心来把能遇到的情况分析一下,只要分析到具体情况了,一下子就豁然开朗了。
如果一直陷入想从整体上寻找找零方案,就会把自己陷进去,各种情况一交叉,只会越想越复杂了。
## 其他语言版本
Java
```java
class Solution {
public boolean lemonadeChange(int[] bills) {
int cash_5 = 0;
int cash_10 = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
cash_5++;
} else if (bills[i] == 10) {
cash_5--;
cash_10++;
} else if (bills[i] == 20) {
if (cash_10 > 0) {
cash_10--;
cash_5--;
} else {
cash_5 -= 3;
}
}
if (cash_5 < 0 || cash_10 < 0) return false;
}
return true;
}
}
```
Python
```python
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
five, ten, twenty = 0, 0, 0
for bill in bills:
if bill == 5:
five += 1
elif bill == 10:
if five < 1: return False
five -= 1
ten += 1
else:
if ten > 0 and five > 0:
ten -= 1
five -= 1
twenty += 1
elif five > 2:
five -= 3
twenty += 1
else:
return False
return True
```
Go
```golang
func lemonadeChange(bills []int) bool {
//left表示还剩多少 下表0位5元的个数 下表1为10元的个数
left:=[2]int{0,0}
//第一个元素不为5直接退出
if bills[0]!=5{
return false
}
for i:=0;i<len(bills);i++{
//先统计5元和10元的个数
if bills[i]==5{
left[0]+=1
}
if bills[i]==10{
left[1]+=1
}
//接着处理找零的
tmp:=bills[i]-5
if tmp==5{
if left[0]>0{
left[0]-=1
}else {
return false
}
}
if tmp==15{
if left[1]>0&&left[0]>0{
left[0]-=1
left[1]-=1
}else if left[1]==0&&left[0]>2{
left[0]-=3
}else{
return false
}
}
}
return true
}
```
Javascript:
```Javascript
var lemonadeChange = function(bills) {
let fiveCount = 0
let tenCount = 0
for(let i = 0; i < bills.length; i++) {
let bill = bills[i]
if(bill === 5) {
fiveCount += 1
} else if (bill === 10) {
if(fiveCount > 0) {
fiveCount -=1
tenCount += 1
} else {
return false
}
} else {
if(tenCount > 0 && fiveCount > 0) {
tenCount -= 1
fiveCount -= 1
} else if(fiveCount >= 3) {
fiveCount -= 3
} else {
return false
}
}
}
return true
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>