赞
踩
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
}
};
把这个题理解成下边的图就可以。
每个节点表示添加的油量,每条边表示消耗的油量。题目的意思就是问我们从哪个节点出发,还可以回到该节点。只能顺时针方向走。
遍历每一个加油站为起点的情况,模拟一圈。如果跑了一圈,中途没有断油,而且最后油量大于等于0,说明这个起点是ok的。
class Solution { public: int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { for (int i = 0; i < cost.size(); ++i) { int rest = gas[i] - cost[i]; // 记录剩余油量 int index = (i + 1) % cost.size(); while (rest > 0 && index != i){// 模拟以i为起点行驶一圈 rest += gas[index] - cost[index]; index = (index + 1) % cost.size(); } // 如果以i为起点跑一圈,剩余油量>=0,返回该起始位置 if(rest >= 0 && index == i){ return i; } } return -1; } };
class Solution { public: int goodArray(vector<int>& gas, vector<int>& cost){ int n = gas.size(); //考虑从每一个点出发 for (int i = 0; i < n; ++i) { int j = i; int remain = gas[i]; while (remain - cost[i] >= 0){ remain = remain - cost[j] + gas[(j + 1) % n]; j = (j + 1) % n; if(j == i){ return i; } } } return false; } };
for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!
思路
这里gas和cost每一个位置都是对应的,并且实际意义上我们只需要知道二者的差值即可。
举个例子,在第i段路,给你2升汽油开车消耗1升汽油,是等价于给你100升汽油开车消耗99升汽油的。
这种情况,如果我们在把gas和cost看作两个数组会形成不必要的麻烦。
题目给了两种状态,首先是汽车起始汽油为0,然后行驶过程中汽车汽油必须足够行驶路程(也就是油表必须为正)。
我们假设油表可以为负值,这样如果存在题目中所说的唯一答案,那么当我们从0行驶到N后,油表为非负。如果油表为负值就返回-1;
那么我们怎么找从哪里开始出发呢?答案就是当我们油表值最小时,从这个点出发,我们的油表初始状态为0,之后也不会低于这个0,因为这个0其实就是油表最小值了。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n = gas.size();
int cur_gas = 0, min_gas = 0, min_index = 0;//默认从0出发
for (int i = 0; i < n; ++i) {
cur_gas = cur_gas + gas[i] - cost[i];
if(cur_gas < min_gas){
min_gas = cur_gas;
min_index = i + 1;
}
}
return cur_gas < 0 ? -1 : min_index;//油箱为负值返回-1;
}
};
思路
有一个环形路上有n个站点; 每个站点都有一个好人或一个坏人; 好人会给你钱,坏人会收你一定的过路费,如果你带的钱不够付过路费,坏人会跳起来把你砍死; 问:从哪个站点出发,能绕一圈活着回到出发点?
class Solution { public: int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { int n = gas.size(); int total = 0; for (int i = 0; i < n; ++i) { total += (gas[i] - cost[i]); } if(total < 0){ return -1; } int start = 0; int run_cost = 0; for (int i = 0; i < n; ++i) { run_cost += gas[i] - cost[i]; if(run_cost < 0){ start = i + 1; run_cost = 0; } } return start; } };
class Solution { public: int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { int curSum = 0; int totalSum = 0; int start = 0; for (int i = 0; i < gas.size(); i++) { curSum += gas[i] - cost[i]; totalSum += gas[i] - cost[i]; if (curSum < 0) { // 当前累加rest[i]和 curSum一旦小于0 start = i + 1; // 起始位置更新为i+1 curSum = 0; // curSum从0开始 } } if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了 return start; } };
加油站,怎么能往下继续走呢???就是我加的油量-取下一站的消耗量>=0,就能继续走
int[] gas = {1,2,3,4,5};
int[] cost = {3,4,5,1,2};
剩余油差:
offset = [-2, -2, -2, 3, 3]
剩余油差的累加和:
sum = [-2, -4, -6, -3, 0, -2, -4, -6, -3, 0]
class Solution { int goodArray(vector<int>& gas, vector<int>& cost){ int N = gas.size(); int M = N << 1; // 为了方便走成一个圈, 我们将累加剩余油叠加2次offset,得到sum std::vector<int> offset(N); for (int i = 0; i < N; ++i) { offset[i] = gas[i] - cost[i]; } std::vector<int> sum(M); //第一圈 int idx = 0; sum[idx++] = offset[0]; for (int i = 1; i < N; ++i) { sum[idx] = sum[idx - 1] + offset[i]; idx++; } //第2圈 sum[idx] = sum[idx - 1] + offset[0]; idx++; for (int i = 1; i < N; i++) { sum[idx] = sum[idx - 1] + offset[i];//累加 idx++; } //得到了双拼的累加油差数组sum之后,开始从i==0--N-1判断,以N长度为窗口,找瓶颈最小值 //这个瓶颈,就是在走一圈累加过程中最瓶颈的地方,如果它>=0,没问题 //注意,首次i==0出发时,累加和瓶颈就是自己 //但是i==1开始,注意累加和已经算上了i==0的点,需要把i-1之前的油量减掉 std::deque<int> deque; for (int i = 0; i < 2 * N; ++i) { //每次一个N代表能走一圈 //一个双端队列,左边放小的数,一旦i比尾部小,直接弹出尾部,保证左边头小 while (!deque.empty() && sum[deque.back()] >= sum[i]){ deque.pop_back(); } deque.push_back(i); //窗口到N就过期,同时收集答案 if(deque.front() == i - N){ deque.pop_front(); } if (i >= N - 1){ //形成了一个窗口 if (i == N - 1){//当窗口左边为0时,说明第一个窗 if (sum[deque.front()] >= 0) return 0; } else {//不是第一个窗口,就看瓶颈了 int min = sum[deque.front()] - sum[i - N];//减掉前面累加油量的瓶颈 if (min >= 0) return i - N + 1;//返回的是那个窗口的第一个位置 } } } return -1; } public: int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { return goodArray(gas, cost); } };
题目 | 思路 |
---|---|
leetcode:134. 加油站,能够走一圈的起点 Gas Station | 滑动窗口(只要窗口内有一个瓶颈>=0,说明能以它为起点走一圈) |
leetcode:780. (sx, ty)能够通过转换到达终点(tx, ty) Reaching Points | 逆向思维,辗转相除法 |
782. 变为棋盘 Transform to Chessboard |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。