示例:
由于每次经过一个加油站都会得到油的补充,同时开往下一站还会消耗一定数量的油。那么不妨计算每个加油站的净增油量。
以gas = [1, 2, 3, 4, 5], cost = [3, 4, 5, 1, 2]为例——
我们可以计算出每一个站点的净增油量,比如站点1,我们获得单位1的油量,同时又消耗单位3的油量。那么我们的净增油量就为-2个单位的油量。同理,我们可以计算出每个站点的净增油量。
在上述例子中,每个站点净增油量就为[-2, -2, -2, 3, 3]
那么可以预见的是,如果我们想要从某点出发并环绕一圈回到出发点的话,那么我们的净增油量肯定是大于或者等于0的。
并且如果我们的净增油量大于或者等于0,那么肯定也是可以环绕一圈的。因为我们无论从任何位置出发,其所净增的油量总是能支撑我们回到出发点,如果净增油量小于0,那么肯定在中途某个点就必须停下来了。但是我们又不能从任意位置出发,比如上述例子的第二号加油站,净增油量为负数,不足以支撑我们到达下一个站点。
那么这样或许就有一个思路了——
如果在净增油量总和都大于0的情况下,如果我们想要跑完一圈,那么我们就是希望不会出现当前净增油量和为负数的情况。
如果已经舍弃了所有为净增油量和为负数的情况,那么剩下的便是结果。
先给出具体代码:
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int startIndex = 0;
int curSum = 0, TotalSum = 0;
//局部和&&全局和
for (int i = 0; i < cost.length; i++) {
//计算经过每个站点的净剩余油量总和
TotalSum += gas[i] - cost[i];
}
//如果净剩余油量总和小于0就是不可能环绕一圈的
if(TotalSum < 0)
return -1;
for (int i = 0; i < cost.length; i++) {
//局部和
//如果局部和小于0,说明从startIndex的位置出发是不可能环绕一圈的
curSum += gas[i] - cost[i];
//出现curSum小于0的位置就重置curSum,并且将starIndex更新为下一个点
//此时下一个点将成为可能的答案
//以此循环直到循环结束,那么最后一个startIndex更新的值就是答案
if(curSum < 0){
startIndex = i + 1;
curSum = 0;
}
}
return startIndex;
}
}
我们从i = 0开始遍历,依次累加curSum,表示当前各站点净增油量总和。当发现累加上当前点后,curSum < 0便直接从下一点开始。
为什么中间的各点可以不遍历呢?万一某点开始curSum大于0呢?
这是不可能的啊——
反证法:
设起始位置为x,使得curSum小于0的点为y。那么在[x, y]区间,如果存在一点r,使得curSum--->[r, y]大于0,那么按照我们的代码逻辑,此点r一定是当取得curSum--->[x, r - 1]小于0后startIndex更新取得的。那么这样的话就不存在从x遍历到y的过程了,因为当我们从x遍历到r - 1的时候就已经更新了。
这与我们假设的过程相矛盾,所以[x, y]中不存在某点使得从当前点开始直到y,curSum大于0。
直接证明:
我们的前提是一定存在从x到y的连续过程。
那么设连续和为Sum = a(x) + a(x + 1) + a(x + 2) + ... + a(y),取其中任意一点x + n开始(n < y - x)我们需要证明的是无论从任意一点(x + n)开始,直到点y,连续和必定小于0。
因为在代码逻辑中,当我们遇到curSum < 0后便结束当前遍历,并从下一点重新开始。那么在我们的前提下,点(x + n)之前的部分连续和一定是大于0的,因为如果小于0,从x开始的遍历便中断了,这不符合我们定义的大前提!
所以点(x + n)之前累加上来的值一定大于0,另其为sum1(sum1 > 0),另从点(x + n)开始直到点y的连续和为sum2。因为总和小于0,即sum1 + sum2 < 0,又因为sum1大于0,所以sum2必定小于0至此我们证明了从任意一定(x + n)开始(n < y - x),直到点y,连续和必定小于0。从而证明万一从某点开始curSum大于0的猜测是不成立的!
所以我们直接舍弃遍历途中中间的各点
那么当我们遍历到数组末尾,最后一次更新的startIndex就是起始位置了。
因为我们的大前提是净增油量总和大于或等于0,将startIndex前面的部分和定义为sum1, 其后包括其本身定义为sum2.那么sum1必定小于0,sum2必定大于0,并且sum2必定大于sum1的绝对值。所以从startIndex可以环绕数组一圈。
不知道这样讲述是否正确,思路参考《代码随想录》卡尔老师,加上了一点自己的理解,此作为对自己对学习贪心一段时间过后的复习与总结。
贪心算法,得这么加油才能跑完全程!LeetCode :134.加油站_哔哩哔哩_bilibili