此题难。我的解法采用的是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; } } };