加油站
问题链接: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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
二、问题分析
最直观的方法就是把所有起点都算一遍。这么做当然可以,但是会增加时间复杂度。如果计算所有起点出发的循环,就很容易发现在多次循环中包含大量重复计算。所以可以把计算结果保存下来,后续如果需要的时候直接取用。可以把这种方法叫做记账法(备忘录法)
对于该问题,因为每走一步都要计算汽油的消耗和添加,那么就创建一个数组,保存从当前位置出发到达下一位置时油箱里的油量。当然,这里就需要一个假设条件了。假如汽车可以向未来贷油,也就是说,汽车没油也能跑。数组中就可以保存负值,负值的含义就是欠下的油。
不难发现,如果debt数组中所有的值相加为负就说明不管从哪里开始走,汽车的油总是不够用的。
接下来考虑和不为0的情况,因为从表面看上去如果数组和大于等于0,是有可能环游一周,但是如果中间出现连续的负数呢?是否有可能使车停在某个位置加不上油?这需要经过证明才能确定。
简单证明:假设debt在某个位置值为正,把他记作d_i,如果在d_i后出现连续的负数,使得d_i加上这些负值的结果为负,那么从i位置出发就无法环游一周。如果数组中有多对这样的案例,那么数组加和最终值一定是负值,否则汽车总能找到一个出发点完成环游。这个证明不是严谨的数学证明,但是是一个推理过程。如果代码通过了所有测试,那么很有可能说明该方法可行。
三、算法及代码
1 class Solution { 2 public int canCompleteCircuit(int[] gas, int[] cost) { 3 int[] debt = new int[gas.length]; 4 int count = 0; 5 for(int i=0;i<gas.length;i++){//构建debt数组并求和 6 debt[i] = gas[i]-cost[i]; 7 count = count + debt[i]; 8 } 9 if(count<0){ 10 return -1; 11 } 12 /* 13 接下来找起始点,找起始点的想法和上面证明可行性用的类似, 14 如果从一个正数出发在与后面的数字求和的过程中出现负数,那么就说明不能从该位置出发 15 */ 16 for(int i=0;i<debt.length;i++){ 17 int x = 0; 18 for(int j=i;j<debt.length;j++){//两个for循环实现循环遍历的功能 19 if(x < 0){ 20 break; 21 }else{ 22 x = x+debt[j]; 23 } 24 } 25 for(int k=0;k<i;k++){ 26 if(x < 0){ 27 break; 28 }else{ 29 x = x+debt[k]; 30 } 31 } 32 if(x >= 0){ 33 return i; 34 } 35 } 36 return 0; 37 } 38 }
测试通过,说明具有可行性