题目描述
示例
思路、算法及代码实现
首先,我们看完题后,想到的方法一定是暴力遍历法,即从头到尾遍历每个加油站,并检查以该加油站为起点,最终能否行驶一周。
但其实如果我们发现一个逻辑上的小性质的话,就可以只遍历一次。
我们在推理这个性质的过程中,先不考虑“油不能为负数”这个限制,
设left[i]=gas[i]-cost[i],当left[i]越小,说明消耗越多。我们从示例2可以看出,当left[i]<0时,说明没有足够的油从第i个点到第i+1个点,所以left<0的点都不能作为起点。此时可以从最后一个left<0的点的后面那一点开始作为起点,并且要求满足后面的总left要能够补偿前面欠下的负的总left,才能保证走完一周,即总油数一定要大于等于总消耗,本题才能有解。因为从0跑到最后一个left<0的位置,总left最小,即这一段的消耗是整条链上最大的,所以从最后一个left<0的点的后面那个点开始作为起点的话,最终剩下的油一定是最多的,所以只需要遍历一次找到最后一个left<0的点即可。
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
left = [gas[i] - cost[i] for i in range(len(cost))]
minValue = 999
minIndex = 0
sumValue = 0 # 总油数-总消耗
# 一举两得:既可以找到最后一个left<0的点,又可以算得(总油数-总消耗)
for j, l in enumerate(left):
sumValue += l
if sumValue < minValue:
minValue = sumValue
minIndex = j
if sumValue < 0:
return -1
else:
return (minIndex + 1) % len(cost) # 因为是一个圆圈,所以要求余数得到序号
小经验
当要“求最后一个负数”时,转化为“和最小”更巧妙;同理,要“求最后一个正数”时,转化为“和最大”更巧妙。