1. 题目描述
-
描述:汽车的初始油量为0,油箱容量无限大,判断是否能够从某一加油站出发,绕环路行驶一周。
输入:每个加油站的油量gas,每个加油站前往下一个加油站的耗油量cost,cost[end]表示最后一个加油站前往第一个加油站的耗油量。
输出:满足条件的起始加油站编号 or -1 -
示例说明:
如下图所示,每个圆圈代表一个加油站,圆圈内的数字为加油站的编号,箭头方向为绕行方向。加油站上方的数字表示当前当油站的油量cost[i],加油站之间的数字表示从上一个加油站前往下一个加油站的耗油量cost[i]。
输入:gas=[1, 2, 3, 4, 5], cost=[3, 4, 5, 1, 2]
输出:3(汽车可从3号加油站出发绕行一周)
分析:如下表所示,汽车从3号加油站出发,沿着3→4→0→1→2→3的顺序可绕行一周返回出发时的加油站。加油站编号(i) 加油站油量(gas) 前往下一个加油站的耗油量(cost) 到达当前加油站时汽车剩余油量 离开当前加油站时汽车剩余油量 0 1 3 8-2=6 6+1=7 1 2 4 7-3=4 4+2=6 2 3 5 6-4=2 2+3=5 3 4 1 0(出发)→5-5=0(结束) 0+4=4(出发) 4 5 2 4-1=3 3+5=8 -
注意:
- 汽车移动过程中(到达加油站和离开加油站时),油箱的油量始终为非负数;
- 从某个车站前往下一个车站的相对油量(gas[i]-cost[i])的值取决于输入;
- 题目可以转换为寻找一个可行的起始加油站,使得油箱剩余油量+相对油量在每一个车站都为非负数。
2. 解题思路—循环队列
- 关键方法:循环队列+元素合并
- 综合分析:
- 循环队列在初始情况下记录汽车在加油站的相对油量,更新过程中记录加油站的累计情况;
- 根据相邻加油站的相对油量判断加油站的性质,根据性质进行累加,逐渐减少循环队列中的元素。
当加油站的相对油量<0时,即汽车无法以该加油站为起点前往下一个加油站,称该加油站为负加油站;
当加油站的相对油量>0时,即汽车可能以该加油站为起点前往下一个加油站,称该加油站为正加油站;
2.1 初始化循环队列
- 分析:
循环队列可以达到题目要求的最后一个加油站的下一个位置是第一个加油站的效果。
通过合并循环队列中相同性质和不同性质的加油站,判断汽车是否能够绕行一周以及记录绕行起点。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
//输入油量信息gas和前往下一站的油耗信息cost
queue<pair<int, int>> q; //创建循环队列,队列中每个元素保存两个值
//初始化时,每个元素中保存(相对油量,当前加油站的编号),相对油量=当前油量-前往下一站的油耗
for (int i = 0; i < gas.size(); i++) q.push(pair<int, int>(gas[i] - cost[i], i));
//初始化循环队列遍历过程的标志变量
int sum = 0, pos = -1; //sum记录多个相邻的加油站的相对油量的累加值,pos记录这些加油站中第一个加油站的编号
bool init = false; //init记录是否完成一次累加
//模块:遍历和修改循环队列
return pos; //输出可行的加油站起点编号 or -1
}
};
2.2 遍历和修改循环队列
- 分析:主要是循环队列中元素的合并
- 当加油站为相同性质的加油站时,可以直接合并;
若相邻加油站( g 1 , g 2 , . . . , g n g_1, g_2, ...,g_n
- 当加油站为相同性质的加油站时,可以直接合并;