Cover image for 面试经典150题 P134 加油站

面试经典150题 P134 加油站


时间轴

时间轴

2025-10-05

init

贪心

题目:

非贪心算法:基于这样一个事实,如果从 i 到某一点 j 之后发现油不够了,那么 i~j 之间的所有点(比如假设 i~j 某一点 k,从 k 出发,少了 i~k 的油量)(从 i 出发能到 k 说明到 k 时油量>=0)更不可能到达 j+1

1234567891011121314151617181920212223242526272829303132333435363738394041424344
#include <vector>using std::vector;class Solution {public:  int canCompleteCircuit(vector<int> &gas, vector<int> &cost) {    int n = gas.size();    // int start = -1;    int start = 0;    int curr_gas;    int i, idx;    // for (int i = 0; i < n; i++) {    //   // 到达i前至少有多少油才能去下一个    //   if (gas[i] - cost[i] >= 0) {    //     start = i;    //     break;    //   }    // }    // if (start == -1) {    //   return -1;    // }    while (start < n) {      curr_gas = 0;      i = 0;      // 走一圈      for (i = 0; i < n; i++) {        idx = (start + i) % n;        curr_gas += gas[idx] - cost[idx];        if (curr_gas < 0) {      // 路线失败          start = start + i + 1; // 直接跳过失败区间          // 成功绕一圈          break;        }      }      if (i == n)        return start;    }    return -1;  }};

标准的贪心解法:我们是要找一个起点 start,使得从 start 出发沿环路绕一圈,油量永远 ≥ 0。

  1. 首先,同样地如果从 i 到某一点 j 之后发现油不够了,那么 i~j 之间的所有点,因为假设 i~j 某一点 k,从 k 出发,少了 i~k 的油量更不可能到达 j+1;

  2. 其次对于全部环路总油量 total_gas:如果 total_gas - total_cost < 0,说明整个环路油量不够,无论起点哪儿都不可能成功 → 返回 -1,如果 total_gas - total_cost >= 0,一定存在唯一一个可以成功的起点,这就是贪心方法最后返回的 start。

  3. 再者,我们从 0 遍历到 n-1 时,已经筛掉了所有不可能的起点。并且题目保证“如果存在解,则保证它是 唯一 的”。若最终 total >= 0,说明从最后一个 start 出发是可行的。这就说明了只需要遍历一遍是可行的。

那为什么只需遍历 0~n-1 就结束了呢?

形象一点来讲:当 curr_gas < 0 时我们总是把起点重置到“油量曲线最低谷”的下一个位置,所以最终的 start 是全局最低点之后的第一个站;而 total >= 0 说明从最低点之后开始,油量永远不会再跌破 0,因此能绕一整圈。

用反证法:假设最终该解法返回 start,在 start~n-1 之间有一点 k,因为结果是唯一的,如果 start 绕不了一圈,而是 k 能绕一圈,但是由于 start 能够到达 k,所以 start 应该也能绕一圈,与前提存在解则唯一矛盾,因此只需遍历只需要在 total>=0 的情况下,存在 start 可以到达 n-1 就是所需结果。

123456789101112131415161718192021222324252627282930
#include <vector>using std::vector;class Solution {public:    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {        int n = gas.size();        int total_diff = 0;       // 总油量差        int curr_gas = 0;    // 当前区间油量差        int start = 0;       // 当前起点        int diff;        for (int i = 0; i < n; i++) {            diff = gas[i] - cost[i];            total_diff += diff;            curr_gas += diff;            // 如果当前区间油量为负,则不可能从 start 到 i+1            if (curr_gas < 0) {                // 下一站作为新的起点                start = i + 1;                curr_gas = 0;            }        }        // 总油量小于 0,说明无解        return (total_diff >= 0) ? start : -1;    }};
评论加载中…