题目一:
解法一:(模拟)
解题思路:这个解法是自己做出来的,可能就是常规暴力解法吧。
- 先声明一个priceNum数组,记录5美元以及10美元的数量,索引0位置为5美元数量,索引1位置为10美元数量
- 当遇到5美元时,priceNum[0]++
- 当遇到10美元时,priceNum[1]++,同时priceNum[0]--,判断priceNum[0]是否小于零,如果小于零,则表示无法找零
- 当遇到20美元时,先判断priceNum[1]是否大于0,因为大于零,表示有10美元,优先找10美元,再priceNum[0]--,判断priceNum[0]是否小于零,如果小于零,则表示无法找零
- 如果priceNum[1]不大于0,则priceNum[0] -= 3,找3个五美元,判断priceNum[0]是否小于零,如果小于零,则表示无法找零
- 最后返回true
完整代码如下:
var lemonadeChange = function(bills) {
let priceNum = [0, 0]
for (let i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
priceNum[0]++
} else if (bills[i] == 10) {
priceNum[1]++
priceNum[0]--
if (priceNum[0] < 0) {
return false
}
} else {
if (priceNum[1] > 0) {
priceNum[1]--
priceNum[0]--
if (priceNum[0] < 0) {
return false
}
} else {
priceNum[0] -= 3
if (priceNum[0] < 0) {
return false
}
}
}
}
return true
};
解法二:(贪心算法:代码随想录)
思路
这是前几天的leetcode每日一题,感觉不错,给大家讲一下。
这道题目刚一看,可能会有点懵,这要怎么找零才能保证完整全部账单的找零呢?
但仔细一琢磨就会发现,可供我们做判断的空间非常少!
只需要维护三种金额的数量,5,10和20。
有如下三种情况:
- 情况一:账单是5,直接收下。
- 情况二:账单是10,消耗一个5,增加一个10
- 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5
此时大家就发现 情况一,情况二,都是固定策略,都不用我们来做分析了,而唯一不确定的其实在情况三。
而情况三逻辑也不复杂甚至感觉纯模拟就可以了,其实情况三这里是有贪心的。
账单是20的情况,为什么要优先消耗一个10和一个5呢?
因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!
所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。
局部最优可以推出全局最优,并找不出反例,那么就试试贪心算法!
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
};
总结
咋眼一看好像很复杂,分析清楚之后,会发现逻辑其实非常固定。
这道题目可以告诉大家,遇到感觉没有思路的题目,可以静下心来把能遇到的情况分析一下,只要分析到具体情况了,一下子就豁然开朗了。
如果一直陷入想从整体上寻找找零方案,就会把自己陷进去,各种情况一交叉,只会越想越复杂了。