LeetCode系列 No.135加油站问题(详解版)

LeetCode系列 No.135加油站问题
题目描述
在一条环路上有 N 个加油站,你有一辆油箱容量无限的的汽车,
其中第 i 个加油站有汽油 gas[i] 升。从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:

从 1 号加油站(索引为 1 处)出发,可获得2 升汽油。此时油箱有 0 + 2 = 2升汽油
开往 2 号加油站,需要消耗4升汽油.车辆无法完成行驶
因此,1不可为起始索引。

从2号加油站(索引为2处)出发,可获得3L汽油.此时邮箱有 0+3=3升汽油
开往3号加油站,此时需要消耗5升汽油,车辆无法完成行驶
因此,2不可以为起始索引

从 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 可为起始索引。

从4号加油站(索引为4处)出发,可获得5升汽油。此时油箱有0+5=5升汽油
开往0号加油站,此时油箱有5-2+1=4升汽油
开往1号加油站,此时油箱有4-3+2=3升汽油
开往2号加油站,需要消耗4升油,车辆无法完成行驶
因此,4不可为起始索引

从0号加油站(索引为0)出发,可获得1升汽油。
开往1号加油站,需要消耗3升汽油,车辆无法完成行驶

返回3。

示例2
输入:
gas = [2,3,4]
cost = [3,4,3]

输出: -1

解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。

我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。

因此,无论怎样,你都不可能绕环路行驶一周。

思路:我们只要有油就能够往下走,定义 一个变量,rest表示从某站出发到达下一站时油箱中剩下的油。只要rest>0,就表示这辆车可以到达下一站。
如果gas[i] >= cost[i],则当前索引i的加油站可以被认为满足了1个条件,后续只要从它开始循环,如果能回到索引i,则这个索引是有效的。

解决方法一:暴力解法

乞丐版代码

 class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost) {
    int len = gas.size();   // 输入数组的长度
    int tar = -1;           // 这是需要返回的加油站索引,tar属于[0, len-1]
    int rest;               // 存放油箱的含油量
    for (int i = 0; i < len; i++)
    {
        // 满足条件说明小车可以从当前加油站出发,接下来开始一个循环,判断小车是否能持续地回到加油站i
        if (gas[i] >= cost[i])  
        {
            int j;                      // 初始化待循环的变量
            rest = gas[j] - cost[j];    // 初始化从加油站i出发时油箱中的油
            for (j = (i+1) % len; j != i; j = (j+1) % len)
            {
                rest += gas[j];     // 小车能从加油站j获取油后油箱的含油量
                rest -= cost[j];    // 小车到达加油站j+1消耗了油之后油箱的含油量
                /* 如果邮箱含油量小于0,说明油箱中的油不足以支撑小车从加油站j到加油站j+1,
                也就是说以加油站i作为起点不可行,在途中会遇到加油站j,此时油箱中的油不足以支撑小车从加油站j到加油站j+1 */
                if (rest < 0)
                    break;
            }

             // 如果j再次等于i,说明以加油站i作为起点可行,那么保存i到返回的变量tar中,然后跳出循环
            if (i == j)    
            { 
                tar = i;
                break;
            }
        }
    }
    // 如何tar还是-1,则说明遍历完所有加油站都没有找到合适的加油站,则返回-1
    return (tar == -1 ? -1 : tar);
}
};

// “乞丐版”思路很简单,但是它最坏的复杂度能达到O(n^(2)),考虑用些trick将复杂度降低。
对乞丐版中for语句的解释
变量j, 是用来使得车站的指向可以循环。
比如,当i为0时,
第一次执行循环体,j=1,表示,到达1号加油站加油
第二次执行循环体,j=2,表示,到达2号加油站加油
第三次执行循环体,j=3,表示,到达3号加油站加油
第四次执行循环提,j=4,表示,到达4号加油站加油
第五次执行循环体,j=0, 表示,回到0号加油站

改进版算法

class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost) {
    int len = gas.size(), rest = 0;     // rest是小车油箱中剩余的油,初始化为0
    int tar = 0;                        // tar存放的是可能合格的加油站索引,初始化为0
    for (int i = 0; i < gas.size(); i++)
    {
    /* 算法主体:
    如果小车油箱中剩余的油rest加上从加油站i获取的油能支撑它从加油站i跑到加油站i+1,那没问题,循环继续;
    如果不足以支撑,那么当前的加油站i不可能是满足要求的加油站,将tar赋为下一个加油站i+1,并将剩余油量重置为0
    */
        if (rest + gas[i] < cost[i])
        {
            tar = i + 1;
            cost[len-1] += cost[i] - gas[i] - rest;
            rest = 0;
        }
        else
        {
            rest += gas[i] - cost[i];
        }
    }
    return (tar == len ? -1 : tar);
}
};

你可能感兴趣的:(LeetCode系列,LeetCode系列)