Skip to content

Latest commit

 

History

History
 
 

133. Gas Station

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

这道题故事性很强,容易无法 get 题目的意思,我们首先把问题简化:

     A(3)
 <3>/ \<4>
(5)C - B(4)
    <5>

假设就三个加油站, A, B, C 分别储油为 3, 4, 5。然后我们故意将耗油顺序定为 AB(4), BC(5), CA(3)。这样,你想从 A, B 出发,几乎不可能。结果只能选择从 C 出发。

我始终认为一个实际的例子能够帮助咱们理解问题情景,干巴巴的语言描述总会让人摸不着头脑。

总上面的例子,咱们自然能摸出点规律来:

  • 如果 cost[i] - gas[i] < 0 显然不行。
  • 根据上一条扩展更通用的情景:tank + cost[i] - gas[i] < 0 的时候,该起点无效。
  • 看起来咱们应该从 {cost[i] - gas[i]} 的最大值开始,概率似乎大一些。(但要注意,找到这一点,需要起码 O(n) 的时间复杂度,未免得不尝试)

综上,为了节约时间,我们显然从第一个加油站开始。设置一个 store{0} 来记录每时每刻的储量。

int tank{0}, start{0}, stored{0};
for (size_t i=0; i<gas.size(); ++i) { // 从头开始迭代每一个加油站
    if ((tank += gas[i] - cost[i]) < 0) { // 说明起点失效
        // 这个时候起点应该从什么地方开始,是 start++ ?
        // 稍微改改上面的实例:
        //      A(3)
        //  <4>/ \<3>
        // (5)C - B(4)
        //     <5>
        // A 可以走到 B,但却无法走到 C,那么我在 B -> C 的时候发现 A 失败,应该从哪里开始? B 吗?显然从 B 也是无法到 C 的。
        // 因为,A 能到 B,此时的 store > 0,如果直接从 B 开始,store = 0,故,如果从 A 到 C 都不可能,显然从 B 到 C 也是不可能的。
        // 所以此时,更聪明的方法是直接以 C 为起点。
        start = i+1;
        // 但此刻有个问题,我们的循环是按之前的起点来设计的,现在我们的起点变了,那么终点也会变,怎么能在有限的循环里,得知是否能走完一圈呢。
        // 借用一下动态规划的技巧,由于是 circular route , 那么开始的终点到现在的终点,所需要的油耗,恰恰等于开始的起点,到新起点所需要的油耗。那么我们就不必重复计算了,积累上次计算结果即可。
        stored += tank;
        tank = 0;
    }
}
return (tank + stored) < 0 ? -1 : start;