【思路】
加油问题,暴力不可取( O(n^2) ),建议采用线性算法,无额外空间占用,只要998,只要998
我们举个栗子
gas = [7, 5, 6, 0, 6]
cost = [3, 0, 0, 18, 2]
对应求差得到
dec = [7-3, 5-0, 6-0, 0-18, 6-2] = [4, 5, 6, -18, 4]
那么我们按照dec这个列表从头到尾遍历一遍,油量初始化为0
i = 0, dec[i] = 4
油量 = 0+4 = 4
i = 1, dec[i] = 5
油量 = 4+5 = 9
i = 2, dec[i] = 6
油量 = 9+6 = 15
i = 3, dec[i] = -18
油量 = 15-18 = -3 (这里允许负数, 为了方便后面分析)
i = 4, dec[i] = 4
油量 = -3+4 = 1
我们可以得到一个油量表 = [4, 9, 15, -3, 1],从这个表中我们可以获取到关键信息:
1. gas和cost列表各自求和,gas总和如果大于等于cost总和,说明可以找到这样一个起始点,使得环绕路线成为可能,否则找不到起始点
那么油量表中的最后一个数值就代表了gas总和与cost总和的差值,就可以判断了
例子中,最后一个是1,说明这样的起点是存在的
2. 油量表在最低值时,如果从这个最低值的下一刻开始出发,能够让油量表的每个值尽量大,比如以i = 4为起点,油量初始化为0, 那么
i = 4, dec[i] = 4
油量 = 0+4 = 4
i = 0, dec[i] = 4
油量 = 4+4 = 8
i = 1, dec[i] = 5
油量 = 8+5 = 13
i = 2, dec[i] = 6
油量 = 13+6 = 19
i = 3, dec[i] = -18
油量 = 19-18 = 1
油量表 = [8, 13, 19, 1, 4]
说明要找到符合条件的起点,只需要在原来的油量表中找到最后一个最小值(如果有多个的话)的位置,再求该位置的下一个即可,如果已经是最后一个,就返回表头位置
例子中,最小值为-3,下标为i = 3,求得起始下标4
【代码】
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int size = gas.size();
int curgas = 0; // 汽车当前油量
int mingas; // 汽车出现过的最小油量
int res = -1;
// 计算差值,累加到当前油量上,求最小油量(可以为负数)的下标,则下一个坐标就可以作为起点
for (int i=0; i<size; i++) {
curgas += gas[i]-cost[i];
if (res == -1 || mingas >= curgas) { // 求最后一个最小值的下标
mingas = curgas;
res = i;
}
}
return (curgas >= 0)? (res+1)%size : -1;
}
};