uacs2024

导航

leetcode134. 加油站

134. 加油站

微信截图_20251202164951

法一:先计算 rest[i] = gas[i] - cost[i] , 判断从某个位置开始的累和是否<0。时间打败了5.17%🤡

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int len = gas.length;
        if(len == 1 && gas[0] >= cost[0])  return 0;
        int[] rest = new int[len];
        for(int i = 0;i < len;++i)  rest[i] = gas[i] - cost[i];
        for(int i = 0;i < len;++i){
            if(rest[i] <= 0)  continue;
            int sum = rest[i];
            for(int j = (i + 1) % len;j != i;j = (j + 1) % len){
                sum += rest[j];
                if(sum < 0)  break;
            }
            if(sum < 0)  continue;
            return i;
        }
        return -1;
    }
}

法二:贪心

微信截图_20251202171153

 

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        int i = 0;
        while (i < n) {
            int curGas = 0; // 当前剩余油量
            int cnt = 0;    // 成功走过的站点数
            // 模拟从i出发走一圈
            while (cnt < n) {
                int j = (i + cnt) % n; // 当前所在站点
                curGas += gas[j] - cost[j];
                if (curGas < 0)  break; // 从i出发无法走完全程
                cnt++;
            }
            if (cnt == n)  return i;// 成功走完一圈
            else  i = i + cnt + 1;// 核心优化:从i出发最远能到达i+cnt,那么i和i+cnt之间的点都可以跳过
        }
        return -1;
    }
}

法三:一次遍历贪心(推荐)

算法思路:

  1. 全局判断:如果总的汽油量小于总消耗量(sum(gas) < sum(cost)),那么无论从哪里出发都不可能完成全程,直接返回 -1
  2. 局部判断:维护一个当前油量 curTank。从站点 0开始遍历,将每个站点的净收益(gas[i] - cost[i])累加到 curTank
  3. 贪心选择起点:如果在某个站点 icurTank变为负数,说明从之前记录的 start起点无法到达 i+1。那么起点绝不可能是 start到 i之间的任何一点,我们将起点候选更新为 i+1,同时将 curTank归零,重新计算
  4. 由于第一步已经保证了有解,遍历结束后 start就是唯一解。
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // totalTank: 记录跑完整个环路,油箱里最终剩下的总油量。
        // 如果它是负数,说明所有加油站的油加起来都不够跑完全程的消耗,肯定无解[5](@ref)。
        int totalTank = 0;

        // curTank: 记录从当前候选起点(start)出发,开到当前位置时,油箱里剩余的油量。
        // 它是我们判断当前起点是否可行的“临时钱包”[7](@ref)。
        int curTank = 0;

        // start: 我们认为最有可能作为成功起点的加油站编号。一开始我们假设从0号站出发。
        int start = 0;

        // 开始逐个遍历每一个加油站
        for (int i = 0; i < gas.length; i++) {
            // 计算经过当前加油站i的“净收益”:加油量 - 到下一站的消耗量。
            int netGas = gas[i] - cost[i];

            // 将本站的净收益累加到总油量池中,用于最后判断全局是否有解。
            totalTank += netGas;
            // 将本站的净收益累加到当前油箱,看从当前起点出发,能否顺利开到下一站。
            curTank += netGas;

            // **关键判断**:如果当前油箱油量小于0了,说明了一件重要的事:
            // “从我们当前选择的起点`start`出发,最远只能开到本站i,无法开到下一站i+1。”
            if (curTank < 0) {
                // 既然从`start`出发连i+1都到不了,那么`start`肯定不是正确起点。
                // 并且,一个非常重要的推论是:从`start`到`i`之间的任何一个站点,也都不能作为起点!
                // 为什么呢?想象一下:从`start`出发到`i`这段路,你甚至可能还攒下了一些油(curTank曾经是正的),这都失败了。如果从一个更靠后、基础更差的中间点出发,肯定更无法弥补在i站出现的油量赤字[4,7](@ref)。
                // 所以,我们不需要再尝试`start`和`i`之间的点,而是直接“跳跃”到下一个最有可能的地方:i+1号站,把它作为新的候选起点。
                start = i + 1;

                // 既然更换了起点,我们就要清空“临时钱包”,从0开始重新计算从这个新起点出发的油量情况。
                curTank = 0;
            }
        }

        // 循环结束后,我们遍历了所有加油站,也找到了一个候选起点`start`。
        // 现在需要进行最终裁决:全局油量是否足够?
        // - 如果总油量totalTank >= 0,说明整个路程在油量上是可行的。那么根据题目保证,我们找到的`start`就是唯一正确的起点[1](@ref)。
        // - 如果总油量totalTank < 0,说明全世界所有的油加起来都不够用,直接返回-1表示无解[5](@ref)。
        return totalTank >= 0 ? start : -1;
    }
}

 

posted on 2025-12-02 17:11  ᶜʸᵃⁿ  阅读(0)  评论(0)    收藏  举报