题目链接: leetcode.cn/problems/ga…
题目描述
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。
提示:
gas.length == n
cost.length == n
1 <= n <= 105
0 <= gas[i], cost[i] <= 104
解析
解释一下题意先,你到达一个站他会给你加上容量为 gas[i]
的汽油,然后你想前往 i + 1
站的话(也就是下一站),就需要耗 cos[i]
升油。如果通过上述规则,你能找到一个出发站,并且再次返回到此站时,过程中的存油量都大于等于 0,就返回这个站。例如假设你从第 i
个站出发的话,如果通过上述规则,你能找到再次到达 i
站。且过程中的存油量大于等于 0,就返回 i
。
暴力搜索的思路是从每一个加油站出发,依次尝试是否能够行驶一圈,直到找到符合条件的解或者遍历完所有加油站。本题不讲解代码,肯定是超时的。
贪心算法的思路是,从某个加油站出发,尽可能地往前走,如果油量不够了,则从当前加油站重新开始计算。如果走完一圈,剩余的油量足够弥补前面的消耗,则说明存在解。 即先定义外循环,从 i = 0 下标开始寻找出发点,在定义一个内循环,判断 i 是否满足出发点的条件,如果不满足,终止内循环,并更新 i 下标,如果满足,直接返回 i 下标,因为本题说了只有一个下标满足。
直接来说本题的贪心是怎么来的
看上图,如果我们从 A 到 C 是可以的,但是到了 D 站存油量变为负数。此刻 A 到 C 中的任意一站都是到不了 D 站的。
证明:我们假设 A 到 C 站中有一个 B 站可以到达 D 站,那么 B 到 D 后, D 点的存油量肯定是大于等于0(假设存油量用 str 表示),所以
str[j+1] >= 0
,又因为 A 站可以到达 B 站,所以 A 站到达 B 站后,B 站的存油量是大于等于0的,即 str[k] >= 0
,那这样一看从 A 到 D 全程的 str 都是大于等于 0 的。所以 A 站可以到达 D 站,与我们刚开始的 A 到不了 D 是相反的,所以假设不成立。
所以在寻找出发站的时候假设我们是从下标为 0 出发的,到了第 4 站却到达不了第 5 站,那么直接从 5 再开始寻找就好了,不需要再考虑 0-5 之间的任何一个站。这就是本题的贪心。
代码步骤:
- 拿到数组长度
- 定义外循环,寻找出发下标
- 定义内循环,判断是否满足出发点条件
- 返回值
代码
class Solution {
public static int canCompleteCircuit(int[] gas, int[] cost) {
int len = gas.length;
//外循环,遍历下标
int i = 0;
while (i < len) {
int sumgas = 0;
int sumcost = 0;
int j = 0;
//内循环,判断是否满足条件
while (j < len) {
//为了防止越界,使用取余方法,如果下标=len,直接跳到0
int k = (i + j) % len;
//存储汽油量
sumgas += gas[k];
//消耗汽油量
sumcost += cost[k];
//判断此刻的汽油量是否为负数,为负数代表此刻的点不满足条件
if (sumgas - sumcost < 0) break;
j++;
}
/*上述代码跳出循环,需要判断是否走完了全程,走完全程代表符合条件,
没走完全程就需要从i + 就 + 1下标开始(也就是终止下标的下一个下标)
*/
if (j == len) {
return i;
} else {
i = i + j + 1;
}
}
return -1;
}
}