在一条环路上有 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 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。
解法一:
使用贪心算法来解决:对于每一个点都进行一个遍历,初始化remain=gas[i];j=i+1;如果remain-cost[j] >0,就进入第二层循环 ;remain=remain-cost[i]+gas[(j+1)%n] 需要注意的是我们要判断是否可以到达起点,需要使用除余操作
public int canCompleteCircuit(int [] gas,int [] cost){
int remain=0;
int n=gas.length;
for(int i=0;i<n;i++){
int j=i;
remain=gas[j];
while(remain-cost[j]>0){
remain=remain-cost[j]+gas[(j+1)%n]; //更新remain
j=(j+1)%n;
if(j==i)
return i;
}
}
return -1;
}
解法二:
上述的贪心算法中我们做了很多次重复的工作,如果能将这些重复的工作记录下来,那每次去调用一下就好,省了很多时间。在该题中我们对于每一个节点都会去计算它最远可以到达的节点和到达那个节点之后所剩余的汽油量,所以我们创建两个数组farindex 用来存储该节点最远可到达的节点,farremain到达那个节点之后所剩余的汽油量。我们初始化farindex=-1
在之后的计算中,如果此时我们所判断的j节点farindex[j]!=-1,说明之前我们记录过那么此时只需要remain=remain+farremain[j] ; j=farindex[j] 同时需要更新这两个数组
public int canCompleteCircuit(int[] gas, int[] cost) {
int n=gas.length;
int remain=0;
int [] farindex=new int[n];
int [] farremain [] =new int [n];
for(int i=0;i<n;i++)
farindex[i]=-1;
for(int i=0;i<n;i++){
int j=i;
remain=gas[i];
while(remain-cost[j] >=0){
remain=remain-cost[j];
j=(j+1)%n;
if(farindex(j)!=-1)
{ remain=remain+farremain[j]; j=farindex[j]; }
else
{ remain=remain+gas[j];}
if(j==i) return i;
}
farindex[i]=j;
farremain[i]=remain;
}
retrun -1;
}
解法三:
将此时gas数组和cost数组看成一个数组 gas[i]-cost[i] (这个数组中的值有正有负),我们的要求就转化成为:
1、该新数组中所有数之和都必须要>=0;
2、在加数组之和的过程中不能出现负数
3、寻找满足前两个条件的起点
sum的变化:
显然,将 0 作为起点肯定是不行的,因为 sum 在变化的过程中小于 0 了,不符合我们「累加和一直大于等于 0」的要求。
那如果 0 不能作为起点,谁可以作为起点呢?
看图说话,图像的最低点最有可能可以作为起点:
如果把这个「最低点」作为起点,就是说将这个点作为坐标轴原点,就相当于把图像「最大限度」向上平移了。
再加上这个数组是环形数组,最低点左侧的图像可以接到图像的最右侧:
参考链接:https://leetcode.cn/problems/gas-station/solutions/2074636/dang-lao-si-ji-xue-hui-liao-tan-xin-suan-8s2g/
思路::
使用了一个新的数组即gas[i]-cost[i] 要求最终这个数组中所有数的和>=0.并且在相加的时候不能出现负数 ,那么如何寻找第一个点就成了该问题的关键;若果sum<beforesum start=start+1(在新数组中加上该数的值<不加上该数的值,那么就判断下一个点)
public int canCompleteCircuit(int[] gas, int[] cost) {
int sum=0,beforsum=0;
int n=gas.length;
int start=0;
for(int i=0;i<n;i++){
sum+=gas[i]-cost[i];
if(sum < beforesum) //如果加上该数的值还变小了,说明此时gas[i]-cost[i]<0 这个点不能作为起点,start=i+1(判断下一个点)
{
start=i+1;
beforesum=sum;
}
}
if(sum<0)
return -1;
return start;
}