加油站
# 📃 题目描述
题目链接:134. 加油站 - 力扣(LeetCode) (leetcode-cn.com) (opens new window)
在一条环路上有 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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
# 🔔 解题思路
# 暴力解法
暴力解法,遍历每一个加油站为起点的情况,模拟一圈。如果跑了一圈,中途没有断油,而且最后油量大于等于0,说明这个起点是 ok 的。
但代码写起来不是很容易,关键是要模拟跑一圈的过程。
for 循环适合模拟从头到尾的遍历,而 while 循环适合模拟环形遍历
另外,首先要明确,总油量是否小于总油耗,如果是则肯定不能走一圈。如果否,那肯定能跑一圈,接下来就是选取具体的其实位置。
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int totalSum = 0;
for (int i = 0; i < gas.length; i ++) {
totalSum += gas[i] - cost[i];
}
if (totalSum < 0) {
// 总油量小于总的消耗,无解
return -1;
}
// 邮箱中剩余的油
int restGas = 0;
// 循环选择出发时的加油站下标
for (int start = 0; start < gas.length; start ++) {
// 以 start 为起点环绕一圈
restGas = gas[start] - cost[start];
int next = (start + 1) % gas.length;
while (restGas > 0 && next != start) {
restGas += (gas[next] - cost[next]);
next = (next + 1) % gas.length;
}
// 如果以 start 为起点跑一圈,剩余油量>=0
if (restGas >= 0 && next == start) {
return start;
}
}
return -1;
}
}
时间复杂度 O(N^2)
# 贪心解法
首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量 restGas[i] (gas[i] - cost[i]) 相加一定是大于等于零的
i 从 0 开始累加 rest[i],和记为 curSum,一旦 curSum 小于零,说明 [0, i] 区间都不能作为起始位置,起始位置从 i+1 算起,并将 curSum 归零重新计算。
那么为什么一旦[i,j] 区间和为负数,起始位置就可以是 j+1 呢
结论 👉 如果选择站点 i
作为起点「恰好」无法走到站点 j
,那么 i
以及 i
和 j
其中间的任意站点 k
都不可能作为起点,起始位置至少要是 j + 1。
比如说,如果从站点1
出发,走到站点5
时油箱中的油量「恰好」减到了负数,那么说明站点1
「恰好」无法到达站点5
;那么你从站点2,3,4
任意一个站点出发都无法到达5
,因为到达站点5
时油箱的油量也必然被减到负数。
证明一下这个结论
假设 restGas
记录当前油箱中剩余油量,如果从站点i
出发(restGas= 0
),走到j
时恰好出现 restGas< 0
的情况,那说明走到 i, j
之间的任意站点 k
时都满足 restGas > 0
,对吧。
如果把 k
作为起点的话,相当于在站点 k
时 restGas = 0
,想一下,从 i
出发走到 k
好歹还是 restGas > 0
,这样都无法达到 j
,现在你还让 k
站点的 restGas = 0
了,那更不可能走到 j
了对吧。也就是说 k
肯定不能是起点。
综上,这个结论就被证明了。
So,我们可以推出局部最优:
- 当前累计的油箱中剩余的油 curSum 一旦小于 0,起始位置至少要是 i+1
全局最优:
- 根据局部最优找到可以跑一圈的起始位置
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int totalSum = 0;
for (int i = 0; i < gas.length; i ++) {
totalSum += gas[i] - cost[i];
}
if (totalSum < 0) {
// 总油量小于总的消耗,无解
return -1;
}
// 累计的油箱中剩余的油 gas[i] - cost[i]
int curSum = 0;
// 起始加油站下标
int start = 0;
for (int i = 0; i < gas.length; i ++) {
curSum += gas[i] - cost[i];
// 当前累加 restGas[i] 的和 curSum 一旦小于 0,起始位置至少要是 i+1
if (curSum < 0) {
// 更新起始位置
start = i + 1;
// curSum 归零
curSum = 0;
}
}
return start;
}
}
时间复杂度 O(N)
🎁 公众号

各位小伙伴大家好呀,叫我小牛肉就行,目前在读东南大学硕士,上方扫码关注公众号「飞天小牛肉」,与你分享我的成长历程与技术感悟~