代码随想录算法训练营-贪心算法-3|134. 加油站、135. 分发糖果、860. 柠檬水找零
局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
1 class Solution: 2 def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: 3 curSum = 0 # 当前累计的剩余油量 4 totalSum = 0 # 总剩余油量 5 start = 0 # 起始位置 6 7 for i in range(len(gas)): 8 curSum += gas[i] - cost[i] 9 totalSum += gas[i] - cost[i] 10 11 if curSum < 0: # 当前累计剩余油量curSum小于0 12 start = i + 1 # 起始位置更新为i+1 13 curSum = 0 # curSum重新从0开始累计 14 15 if totalSum < 0: 16 return -1 # 总剩余油量totalSum小于0,说明无法环绕一圈 17 return start
1. 一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼。
2. 局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果。
- 时间复杂度: O(n)
- 空间复杂度: O(n)
1 class Solution: 2 def candy(self, ratings: List[int]) -> int: 3 candyVec = [1] * len(ratings) 4 5 # 从前向后遍历,处理右侧比左侧评分高的情况 6 for i in range(1, len(ratings)): 7 if ratings[i] > ratings[i - 1]: 8 candyVec[i] = candyVec[i - 1] + 1 9 10 # 从后向前遍历,处理左侧比右侧评分高的情况 11 for i in range(len(ratings) - 2, -1, -1): 12 if ratings[i] > ratings[i + 1]: 13 candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1) 14 15 # 统计结果 16 result = sum(candyVec) 17 return result
- 情况一:账单是5,直接收下。
- 情况二:账单是10,消耗一个5,增加一个10。
- 情况三:账单是20,优先消耗一个10和一个5(贪心思想),如果不够,再消耗三个5。
1. 因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!
2. 局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。
- 时间复杂度: O(n)
- 空间复杂度: O(1)
1 class Solution: 2 def lemonadeChange(self, bills: List[int]) -> bool: 3 five = 0 4 ten = 0 5 twenty = 0 6 7 for bill in bills: 8 # 情况一:收到5美元 9 if bill == 5: 10 five += 1 11 12 # 情况二:收到10美元 13 if bill == 10: 14 if five <= 0: 15 return False 16 ten += 1 17 five -= 1 18 19 # 情况三:收到20美元 20 if bill == 20: 21 # 先尝试使用10美元和5美元找零 22 if five > 0 and ten > 0: 23 five -= 1 24 ten -= 1 25 #twenty += 1 26 # 如果无法使用10美元找零,则尝试使用三张5美元找零 27 elif five >= 3: 28 five -= 3 29 #twenty += 1 30 else: 31 return False 32 33 return True