题目:
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
理解题目:
方法一:暴力
假设我们从第一个加油站出发,我们通过更新每次剩余的油量,去看能不能返回到原地:
如果可以返回到原地,则说明可以从第一个加油站出发;
如果不可以返回到原地,则从第二个加油站出发,依次遍历,从第三个,第四个……
public int canCompleteCircuit(int[] gas, int[] cost) {
int j;
int rest;
for (int i = 0; i < gas.length; i++) {
rest = gas[i];
j = i;
while (rest >= cost[j]) {
rest += gas[(j + 1) % gas.length] - cost[j];
j = (j + 1) % gas.length;
if (j == i)
return i;
}
}
return -1;
}
通过上述暴力算法我们可以发现,当我们每次从第i
个加油站出发, 在用j
跟踪它最远能到达的地方时,j
要遍历所有不是i
的加油站,直到剩余汽油少于0。假设我们从第一和第二个加油站出发,已经被证明不能够回到原点,那么当我们从第3个加油站出发时,如果能够回到原点,那么就意味着j
跟踪到了第1个加油站和第2个加油站。而第一和第二个加油站我们在从他们出发时就已经遍历过了,这样我们可以将他们记忆起来,当j
再次遍历到他们,就不必再重复计算。于是我们有了方法二。
方法二:暴力算法优化一
public int canCompleteCircuit1(int[] gas, int[] cost) {
int[] far = new int[gas.length];
for (int i = 0; i < gas.length; i++) {
far[i] = -1;
}
int[] far_rest = new int[gas.length];
int j;
int rest;
for (int i = 0; i < gas.length; i++) {
rest = gas[i];
j = i;
while (rest >= cost[j]) {
rest += gas[(j + 1) % gas.length] - cost[j];
j = (j + 1) % gas.length;
if (far[j] != -1) {
rest += far_rest[j];
if (rest >= 0)
j = far[j];
}
if (j == i)
return i;
}
}
return -1;
}
方法二主要优化的是一次遍历中的j
。
我们再进行优化,这次我们优化的结束一次遍历进行再次遍历时的情况,在暴力算法中,当结束一次遍历再次进行一次遍历时,我们遍历的是下一个加油站,我们令j
=i
,重新遍历。
现在我们换个想法,j
保持在原处不变,由于从出发的加油站并不能返回原地,因此我们把出发的加油站删去。然后我们看剩余量是不是少于0,如果少于零则 i 继续前移。也就是方法三。
方法三:暴力算法优化二
public int canCompleteCircuit2(int[] gas, int[] cost) {
int[] diff = new int[gas.length];
for (int i = 0; i < gas.length; i++)
diff[i] = gas[i] - cost[i];
if (gas.length == 1 && diff[0] >= 0)
return 0;
int start = 0, end = 1;
int rest = diff[start];
while (end != start || rest < 0) {
while (rest < 0 && start != end) {
rest = rest - diff[start];
start = (start + 1) % gas.length;
if (start == 0)
return -1;
}
rest = rest + diff[end];
end = (end + 1) % gas.length;
}
return start;
}
好像还能继续优化,
如果i
最远只能到达j
,那么从 i+1
到j-1
中任意一点出发都是不能够返回原点的。
假设·i+1
可以返回原地,那么它一定能够到达j+1
,又因为 ·i
可以到达i+1
,所以i
可以到达j+1
,这与i
最远到达j
是矛盾的。
所以我们不用再像方法三一样将i
往前慢慢移,而是直接令其为j
。
有空再写。