【Classic 150 刷题计划】 LeetCode 134. 加油站 | C++ 贪心算法与局部否决逻辑

LeetCode 134. 加油站

📌 题目描述

题目级别:中等

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

  • 示例 1:
    输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
    输出: 3

💡 破题思路:贪心算法 (一次遍历)

如果使用暴力解法,枚举每个节点为起点去模拟跑一圈,时间复杂度是 O(N2)O(N^2)O(N2),会超时。

事实上,我们可以通过推导得出两个极其强大的结论,从而将复杂度降为 O(N)O(N)O(N):

  1. 总和 >= 0 必有解 :

    如果整条环路的总油量 sum(gas) 大于等于总消耗 sum(cost),那么一定存在一个起点能够跑完一圈。所以我们需要一个变量 tolGas 来记录全局的净油量,只要遍历结束后 tolGas >= 0,我们就放心大胆地返回计算出的起点。

  2. 局部亏损引发的连坐否决 :

    假设我们从起点 i 出发,一路开到了节点 j,但在准备前往 j+1 时发现油箱里的油变负数了。

    这不仅仅意味着 i 不能作为起点,更意味着从 i 到 j 之间的任何一个节点都不能作为起点!

    为什么?因为我们从 i 能够开到中间的节点 k,说明到达 k 时油箱里多多少少带着点剩余的油。带着剩余的油从 k 出发最终都倒在了 j,如果你清空油箱直接从 k 重新出发,绝对会死得更早。

    贪心策略 :一旦在节点 j 处 curGas < 0,直接将下一次尝试的起点跳跃到 j + 1,并将 curGas 清零重新累加。


💻 C++ 代码实现 (原汁原味作者版)

cpp 复制代码
class Solution {
public:
    int canCompleteCircuit(std::vector<int>& gas, std::vector<int>& cost) {
        int n = gas.size();
        
        // 记录从当前起点 st 出发后的局部净油量
        int curGas = 0;
        // 记录跑完全程的全局净油量
        int tolGas = 0;
        // 记录潜在的起点
        int st = 0;
        
        for (int i = 0; i < n; i ++ )
        {
            tolGas += gas[i] - cost[i];
            curGas += gas[i] - cost[i];

            // 一旦局部油量跌破 0,触发"连坐否决"
            if (curGas < 0)
            {
                curGas = 0; // 油箱清空,从下一个站重新开始计算
                st = i + 1; // [st, i] 区间内的所有站点全军覆没,新起点只能是 i + 1
            }
        }
        
        // 只要全局油量 >= 0,最终锁定下来的 st 就绝对是合法的
        return tolGas >= 0 ? st : -1;
    }
};
相关推荐
汉克老师40 分钟前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
文丑颜不良啊2 小时前
LeetCode2861~2880题目解析
java·leetcode
晴天的雨.9922 小时前
【C++】继承篇
java·开发语言·c++
青少儿编程课堂2 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会图形化考级:校园大富翁经营闯关解析
c++·python·算法·bfs·信息学竞赛
another heaven2 小时前
【C/C++ 第一次跨越(上):用 C++11/14 停止制造新的旧代码】
c语言·c++·制造
qq_406981032 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
I_belong_to_jesus13 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Rabitebla14 小时前
【Linux】进程控制(十二) 自定义 Shell(二):内建命令与进程工作目录管理
linux·c++·笔记·学习·bash
码客-ygluu20 小时前
一个通用游戏数据服务器
c++·架构
another heaven20 小时前
【C/C++ Ranges 是零开销抽象吗?拆开管道、惰性求值和编译期膨胀】
c语言·开发语言·c++