Skip to main content

134. 加油站 [medium]

134. 加油站 [medium]

https://leetcode-cn.com/problems/gas-station/

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

如果题目有解,该答案即为唯一答案。 输入数组均为非空数组,且长度相同。 输入数组中的元素均为非负数。 示例 1:

输入: 

gas = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

示例 2:

输入: 
gas = [2,3,4]
cost = [3,4,3]

输出: -1

解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

通过次数36,813 | 提交次数69,169

Second Try

参考题解才了解的解法,但是想明白还是花了很多时间.

acc_zone区间的选择

  • 如果acc_zone在i处小于0,说明不管从i前的哪一个index开始,都会小于0,只能放弃这部分内容。
  • 因为之前在该区间能顺利走到i,说明走的每一步都有大于0的余值,从该区间任意部分重新开始,只会减少前面的余值,必然走不过去最后那个坎。

acc<0必定无解,至于acc>=0必定有解,其实想一想有时候反而要犯迷惑。

  • [start_p, n) 必定大于0

  • 假定存在k使得无解,则[start_p, n) + [0, k] < 0;但是对于剩下的部分(k, start_p),不管其中有多少个失败的区间(可能不止一个),必然<0(多个失败区间的和小于零且小于任意一个区间)

  • 因此[0, k] + (k, start_p) + [start_p, n) < 0, 与 acc > 0相悖,故必然有解。

这个思路流程还真是绕啊,怎么就能一开始就想到。

class Solution(object):
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int

参考题解才了解的解法,但是想明白还是花了很多时间
如果acc_zone在i处小于0,说明不管从i前的哪一个index开始,都会小于0,只能放弃这部分内容。
因为之前在该区间能顺利走到i,说明走的每一步都有大于0的余值,从该区间任意部分重新开始,只会减少前面的余值,必然走不过去最后那个坎。

acc<0必定无解,至于acc>=0必定有解,其实想一想有时候反而要犯迷惑。
[start_p, n) 必定大于0
假定存在k使得无解,则[start_p, n) + [0, k] < 0;但是对于剩下的部分(k, start_p),不管其中有多少个失败的区间(可能不止一个),必然<0(多个失败区间的和小于零且小于任意一个区间)
因此[0, k] + (k, start_p) + [start_p, n) < 0, 与 acc > 0相悖,故必然有解。
这个思路流程还真是绕啊,怎么就能一开始就想到。
"""
start_p = 0
acc = 0
acc_zone = 0
for i in range(len(gas)):
acc += gas[i] - cost[i]
acc_zone += gas[i] - cost[i]
if acc_zone < 0:
acc_zone = 0
# no need to worry about i + 1 > n - 1, because this situation means no solution which will be checked by acc
start_p = i + 1
return start_p if acc >= 0 else -1
  • 执行用时:16 ms, 在所有 Python 提交中击败了95.99%的用户
  • 内存消耗:13.5 MB, 在所有 Python 提交中击败了25.00%

First Try

2020-06-23

暴力算法,一次通过,也是很惊讶。环形问题,只要搞个两倍长数组就很容易解决了。边界条件range(start_p, start_p + len(gas))需要注意,每个点能通过已经代表其能迈向下一个点。

class Solution(object):
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""

rgas = gas + gas
rcost = cost + cost
for start_p in range(len(gas)):
if rgas[start_p] < rcost[start_p]:
continue
acc_gas = 0
succ = True
for i in range(start_p, start_p + len(gas)):
acc_gas = acc_gas + rgas[i] - rcost[i]
if acc_gas < 0:
succ = False
break
if succ:
return start_p
return -1
  • 执行用时:1412 ms, 在所有 Python 提交中击败了31.86%的用户
  • 内存消耗:14 MB, 在所有 Python 提交中击败了25.00%