題目:
在一條環路上有 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
。
有空再寫。