此题难。我的解法采用的是O(n)的算法,就是两个指针,end向右走,如果可以,继续向右走,如不行,start向左走。这里的关键是,如果从start走到end,total是负数,那么从任意一个中间的节点出发到end,仍然不可行。因为按照这个走法,从start到中间任何一个节点,total都是正数;有这个正数加成到达end尚且是负数,从中间节点直接走更是不行。
写起来也不容易,length为1以及(gas-cost)全是负数的情况不好处理,后来发现加一个valid的boolean标签就可解决了。代码中start表示起始点,end表示下一个要判断的点。
我的代码是用while(true) + break实现的,也可以用i来表示下一个要更新gas的点是end还是start,类似:http://blog.csdn.net/fytain/article/details/12191103
public class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int start = 0;
int end = 0;
int len = gas.length;
int total = 0;
boolean valid = false;
while (true)
{
if (total + gas[end] - cost[end] >= 0)
{
total += (gas[end] - cost[end]);
end = (end + 1) % len;
valid = true;
if (start == end) break;
}
else
{
start = (start + len - 1) % len;
valid = false;
if (start == end) break;
total += (gas[start] - cost[start]);
}
}
if (valid) return start;
else return -1;
}
}
然后在discuss里面有个比较巧妙的算法:它基于两点,1. 如果total>0,那么一定有解(需证明);2. 只要记录从目前到结尾的sum为正数的节点就行;
int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
int sum = 0;
int total = 0;
int j = -1;
for(int i = 0; i < gas.size() ; ++i){
sum += gas[i]-cost[i];
total += gas[i]-cost[i];
if(sum < 0){
j=i; sum = 0;
}
}
return total>=0? j+1 : -1;
}
第二刷,写的简单了点。一是理解了只要能够最终total>=0,说明总能找到一个点,能够走完(否则就平移可以了),而是开始的点是一个转折点,就是之前的那个点是最小值了。
class Solution {
public:
int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {
int start = -1;
int size = gas.size();
int remaining = 0;
int min_val = 0;
int min_idx = -1;
bool hasSolution = false;
for (int i = 0; i < size; i++) {
remaining = remaining + gas[i] - cost[i];
if (remaining >= 0) {
hasSolution = true;
} else {
hasSolution = false;
if (remaining < min_val) {
min_val = remaining;
min_idx = i;
}
}
}
if (!hasSolution) {
return -1;
} else {
return (min_idx + 1) % size;
}
}
};