文章目录
题目
标题和出处
标题:加油站
出处:134. 加油站
难度
6 级
题目描述
要求
在一条环路上有 n \texttt{n} n 个加油站,其中第 i \texttt{i} i 个加油站有汽油 gasi \texttt{gasi} gasi 升。
有一辆油箱容量无限的的汽车,从第 i \texttt{i} i 个加油站开往第 i + 1 \texttt{i} + \texttt{1} i+1 个加油站需要消耗汽油 costi \texttt{costi} costi 升。从其中的一个加油站出发,初始时油箱为空。
给定两个整数数组 gas \texttt{gas} gas 和 cost \texttt{cost} cost,如果可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 \texttt{-1} -1。如果存在解,则保证 解是唯一的。
示例
示例 1:
输入: gas = 1,2,3,4,5, cost = 3,4,5,1,2 \texttt{gas = 1,2,3,4,5, cost = 3,4,5,1,2} gas = 1,2,3,4,5, cost = 3,4,5,1,2
输出: 3 \texttt{3} 3
解释:
从 3 \texttt{3} 3 号加油站(下标 3 \texttt{3} 3)出发,可获得 4 \texttt{4} 4 升汽油。此时油箱有 0 + 4 = 4 \texttt{0} + \texttt{4} = \texttt{4} 0+4=4 升汽油。
开往 4 \texttt{4} 4 号加油站,此时油箱有 4 − 1 + 5 = 8 \texttt{4} - \texttt{1} + \texttt{5} = \texttt{8} 4−1+5=8 升汽油。
开往 0 \texttt{0} 0 号加油站,此时油箱有 8 − 2 + 1 = 7 \texttt{8} - \texttt{2} + \texttt{1} = \texttt{7} 8−2+1=7 升汽油。
开往 1 \texttt{1} 1 号加油站,此时油箱有 7 − 3 + 2 = 6 \texttt{7} - \texttt{3} + \texttt{2} = \texttt{6} 7−3+2=6 升汽油。
开往 2 \texttt{2} 2 号加油站,此时油箱有 6 − 4 + 3 = 5 \texttt{6} - \texttt{4} + \texttt{3} = \texttt{5} 6−4+3=5 升汽油。
开往 3 \texttt{3} 3 号加油站,消耗 5 \texttt{5} 5 升汽油,正好足够返回到 3 \texttt{3} 3 号加油站。
因此,返回 3 \texttt{3} 3 作为起始下标。
示例 2:
输入: gas = 2,3,4, cost = 3,4,3 \texttt{gas = 2,3,4, cost = 3,4,3} gas = 2,3,4, cost = 3,4,3
输出: -1 \texttt{-1} -1
解释:
不能从 0 \texttt{0} 0 号或 1 \texttt{1} 1 号加油站出发,因为没有足够的汽油可以行驶到下一个加油站。
从 2 \texttt{2} 2 号加油站出发,可获得 4 \texttt{4} 4 升汽油。此时油箱有 0 + 4 = 4 \texttt{0} + \texttt{4} = \texttt{4} 0+4=4 升汽油。
开往 0 \texttt{0} 0 号加油站,此时油箱有 4 − 3 + 2 = 3 \texttt{4} - \texttt{3} + \texttt{2} = \texttt{3} 4−3+2=3 升汽油。
开往 1 \texttt{1} 1 号加油站,此时油箱有 3 − 3 + 3 = 3 \texttt{3} - \texttt{3} + \texttt{3} = \texttt{3} 3−3+3=3 升汽油。
无法返回 2 \texttt{2} 2 号加油站,因为返程需要消耗 4 \texttt{4} 4 升汽油,但是油箱只有 3 \texttt{3} 3 升汽油。
因此,无论从哪个加油站出发,都不可能绕环路行驶一周。
数据范围
- n = gas.length = cost.length \texttt{n} = \texttt{gas.length} = \texttt{cost.length} n=gas.length=cost.length
- 1 ≤ n ≤ 10 5 \texttt{1} \le \texttt{n} \le \texttt{10}^\texttt{5} 1≤n≤105
- 0 ≤ gasi, costi ≤ 10 4 \texttt{0} \le \texttt{gasi, costi} \le \texttt{10}^\texttt{4} 0≤gasi, costi≤104
解法
思路和算法
环路上有 n n n 个加油站,需要找到可以绕环路行驶一周的出发加油站的编号。朴素的做法是分别尝试从每个加油站出发判断是否可以绕环路行驶一周,该做法需要尝试 n n n 个起点,每个起点需要 O ( n ) O(n) O(n) 的时间遍历和判断,时间复杂度是 O ( n 2 ) O(n^2) O(n2),该时间复杂度过高,需要优化。
为了降低时间复杂度,需要跳过不可能作为起点的加油站。
当汽车位于第 i i i 个加油站时,获得 gas i \textit{gas}i gasi 升汽油,行驶到下一个加油站需要消耗 cost i \textit{cost}i costi 升汽油,因此汽车从第 i i i 个加油站行驶到下一个加油站的汽油变化量是 gas i − cost i \textit{gas}i - \textit{cost}i gasi−costi 升。初始时油箱中的汽油是零,如果行驶到一个加油站的剩余汽油是负数,则无法行驶到该加油站。
汽车绕环路行驶一周的汽油变化量是 sum = ∑ i = 0 n − 1 ( gas i − cost i ) \textit{sum} = \sum_{i = 0}^{n - 1} (\textit{gas}i - \textit{cost}i) sum=∑i=0n−1(gasi−costi)。如果 sum < 0 \textit{sum} < 0 sum<0,则无论从哪个加油站出发,绕环路行驶一周之后的剩余汽油都是负数,因此不可能绕环路行驶一周,返回 − 1 -1 −1。如果 sum ≥ 0 \textit{sum} \ge 0 sum≥0,则一定存在一个加油站作为起点,可以绕环路行驶一周。
如果汽车从第 i i i 个加油站出发可以行驶到第 j j j 个加油站且不能行驶到第 j + 1 j + 1 j+1 个加油站,其中 i ≠ j i \ne j i=j 且从第 i i i 个加油站行驶到第 j j j 个加油站经过的加油站数量小于 n n n,则从第 i i i 个加油站到第 j j j 个加油站都不可能作为起点,将第 j + 1 j + 1 j+1 个加油站作为可能的起点继续尝试。理由如下。
汽车位于第 i i i 个加油站时的汽油是零,位于从第 i + 1 i + 1 i+1 个加油站到第 j j j 个加油站的任意一个加油站时汽油都是非负数,位于第 j + 1 j + 1 j+1 个加油站时汽油是负数。用 f ( x , y ) f(x, y) f(x,y) 表示汽车从第 x x x 个加油站行驶到第 y y y 个加油站的汽油变化量,则 f ( i , j ) ≥ 0 f(i, j) \ge 0 f(i,j)≥0 且 f ( i , j + 1 ) < 0 f(i, j + 1) < 0 f(i,j+1)<0。在从第 i i i 个加油站到第 j j j 个加油站的途中任选一个加油站,记为第 k k k 个加油站,则 f ( i , j ) = f ( i , k ) + f ( k , j ) f(i, j) = f(i, k) + f(k, j) f(i,j)=f(i,k)+f(k,j), f ( i , k ) ≥ 0 f(i, k) \ge 0 f(i,k)≥0,因此 f ( k , j ) ≤ f ( i , j ) f(k, j) \le f(i, j) f(k,j)≤f(i,j), f ( k , j + 1 ) = f ( k , j ) + f ( j , j + 1 ) ≤ f ( i , j ) + f ( j , j + 1 ) = f ( i , j + 1 ) < 0 f(k, j + 1) = f(k, j) + f(j, j + 1) \le f(i, j) + f(j, j + 1) = f(i, j + 1) < 0 f(k,j+1)=f(k,j)+f(j,j+1)≤f(i,j)+f(j,j+1)=f(i,j+1)<0,即如果从第 k k k 个加油站出发则一定不可能行驶到第 j + 1 j + 1 j+1 个加油站,因此第 k k k 个加油站不能作为起点,可能作为起点的加油站一定在第 j j j 个加油站之后。
遍历过程中维护当前作为起点的加油站下标 start \textit{start} start,初始时 start = 0 \textit{start} = 0 start=0。当 start = i \textit{start} = i start=i 时,如果汽车从第 i i i 个加油站出发可以行驶到第 j j j 个加油站且不能行驶到第 j + 1 j + 1 j+1 个加油站,则将 start \textit{start} start 的值更新为 j + 1 j + 1 j+1,继续遍历。遍历结束之后,如果 sum ≥ 0 \textit{sum} \ge 0 sum≥0,则从下标 start \textit{start} start 开始一定可以绕环路行驶一周。理由如下。
-
如果 start = 0 \textit{start} = 0 start=0,则遍历过程中任意时刻的汽油都是非负数,因此可以绕环路行驶一周。
-
如果 start > 0 \textit{start} > 0 start>0,则从下标 start \textit{start} start 到下标 n − 1 n - 1 n−1 的过程中任意时刻的汽油都是非负数。假设存在下标 j j j 满足 j < start j < \textit{start} j<start 且行驶到下标 j j j 处的汽油是负数,则将行程分成三段,第一段从下标 start \textit{start} start 到下标 n − 1 n - 1 n−1,第二段从下标 n − 1 n - 1 n−1 到下标 j j j,第三段从下标 j j j 到下标 start \textit{start} start,此时第三段的汽油变化一定是正数,一定可以在第三段找到一个加油站作为起点,在遍历到 start \textit{start} start 之前就会遍历到正确的起点。因此不存在下标 j j j 使得行驶到下标 j j j 处的汽油是负数。
根据上述分析,可以使用贪心的思想寻找作为起点的加油站。
从下标 0 0 0 开始,从左到右遍历数组 gas \textit{gas} gas 和 cost \textit{cost} cost,遍历过程中维护起点 start \textit{start} start、汽油变化量总和 sum \textit{sum} sum 与当前剩余汽油量 remain \textit{remain} remain,初始时 start \textit{start} start、 sum \textit{sum} sum 与 remain \textit{remain} remain 都是 0 0 0。当遍历到下标 i i i 时,执行如下操作。
-
计算 difference = gas i − cost i \textit{difference} = \textit{gas}i - \textit{cost}i difference=gasi−costi,将 sum \textit{sum} sum 和 remain \textit{remain} remain 分别加 difference \textit{difference} difference。
-
如果 remain < 0 \textit{remain} < 0 remain<0,则将 start \textit{start} start 更新为 i + 1 i + 1 i+1,将 remain \textit{remain} remain 更新为 0 0 0。
遍历结束之后,如果 sum ≥ 0 \textit{sum} \ge 0 sum≥0 则返回 start \textit{start} start,否则返回 − 1 -1 −1。
代码
java
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int start = 0;
int sum = 0, remain = 0;
int n = gas.length;
for (int i = 0; i < n; i++) {
int difference = gas[i] - cost[i];
sum += difference;
remain += difference;
if (remain < 0) {
start = i + 1;
remain = 0;
}
}
return sum >= 0 ? start : -1;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 gas \textit{gas} gas 和数组 cost \textit{cost} cost 的长度。需要遍历两个数组一次。
-
空间复杂度: O ( 1 ) O(1) O(1)。