【LeetCode-贪心】加油站
题目描述
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
- 如果题目有解,该答案即为唯一答案。
- 输入数组均为非空数组,且长度相同。
- 输入数组中的元素均为非负数。
示例:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
题目链接: https://leetcode-cn.com/problems/gas-station/
思路1
假设起始点为 i,则起始点的油量 gas[i] 一定是大于等于消耗 cost[i] 的,所以我们先找到所有可能为起始点的位置。
记录当前油量 curGas,对于每一个起始点:
- curGas = curGas + gas[i] - cost[i];
- 如果 curGas 小于 0,则说明该起始点不符合要求;尝试下一个起始点;否则返回起始点;
如果所有的起始点都不行,则返回 -1.
代码如下:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
vector<int> starts;
int n = gas.size();
for(int i=0; i<n; i++){
if(gas[i]>=cost[i]) starts.push_back(i); // 找到所有可能的起始点
}
for(int i=0; i<starts.size(); i++){ // 尝试每一个可能的起始点
int departion = starts[i];
int curPos = starts[i];
int curGas = gas[curPos] - cost[curPos];
curPos = (curPos+1)%n;
bool flag = true;
while((curPos)%n!=departion){
curGas += gas[curPos];
curGas -= cost[curPos];
if(curGas<0){
flag = false;
break;
}
curPos = (curPos+1)%n;
}
if(flag) return departion;
}
return -1;
}
};
思路2
使用两个变量 totalGas 记录总的油量剩余,如果跑一边 totalGas<0,则说明无解;使用 curGas 表示从起始点 start 出发到当前位置 i 剩余的油量。如果 curGas<0,则说明从之前的起始点到当前位置不可行,需要寻找新的起始点,新的起始点设为 i+1.
这里有一个问题就是为什么新的起始点要设置为 i+1,而不设置为 [start, i] 之间的点,假设 j ∈ [start, i],则从 j 出发到 i 的 curGas 也会是小于 0 的。
当遍历结束时,如果 totalGas>0,则说明有解,返回记录的起始点;否则返回 -1.
代码如下:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int totalGas = 0;
int curGas = 0;
int start = 0;
for(int i=0; i<gas.size(); i++){
int rest = gas[i] - cost[i];
totalGas += rest;
curGas += rest;
if(curGas<0){
curGas = 0;
start = i+1;
}
}
return totalGas<0? -1:start;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)