zoukankan      html  css  js  c++  java
  • Leetcode: Gas Station

    There are N gas stations along a circular route, where the amount of gas at station i is gas[i].
    
    You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.
    
    Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.
    
    Note:
    The solution is guaranteed to be unique.

    难度:60.这道题用Brute Force的方法解比较好想,就是从每一个站开始,一直走一圈,累加过程中的净余的油量,看它是不是有出现负的,如果有则失败,从下一个站开始重新再走一圈;如果没有负的出现,则这个站可以作为起始点,成功。可以看出每次需要扫描一圈,对每个站都要做一次扫描,所以时间复杂度是O(n^2)。

    brute force: 这道题如果17-18行把分情况讨论改成:sum += balance[(k+j)%N], 这样就会TLE。

     1 public class Solution {
     2     public int canCompleteCircuit(int[] gas, int[] cost) {
     3         int N = gas.length;
     4         int[] balance = new int[N];
     5         for (int i = 0; i < N; i++) {
     6             balance[i] = gas[i] - cost[i];
     7         }
     8         
     9         for (int k = 0; k < N; k++) {
    10             if (check(balance, k, N) == 1) return k;
    11         }
    12         return -1;
    13     }
    14     
    15     public int check(int[] balance, int k, int N) {
    16         int sum = 0;
    17         for (int j = 0; j < N; j++) {
    18             if (k + j < N) sum = sum + balance[k+j];
    19             else sum = sum + balance[k+j-N];
    20             if (sum < 0) return 0;
    21         }
    22         return 1;
    23     }
    24 }

    Gode Ganker有O(N)的方法,优化解法更像是一个数学题,需要定义数学模型并证明命题正确性,比较需要数学逻辑的功底。通过定义的模型以及证明的命题来做

    方法主要思想是把这个圈划分成一个个的负序列,以及一个正序列(如果存在的话)。从任意一个站出发,我们可以累加油的净余量,如果出现负的,序列结束,开启一个新的,并且证明旧的这个序列的起点不能作为起点,因为会出现负油量,不能继续前进。下面我们证明


    不仅这个负序列的起点不能作为起点,负序列中的任意一点都不能作为起点。


    证明:
    假设我们取定负序列中的一个站作为起点,因为一个序列一旦遇到负的净余量就会结束并且开启新的,那么说明在这个起点前的累加结果必然是正数(否则会结束这个序列,则前面不会是这个序列的一部分)。如此我们从当前序列出发必然会使走到序列终点时负的油量更大,本来已经是负的,所以不能去负序列的任意一个结点作为起点。
    根据上面的划分方式,我们会把圈分成一段段的序列,而且其中最多只有一个正序列,那就是绕一圈回到起点的那个序列(当然也有可能整个圈是一个正序列,就是油量一直为正,那么我们测的开始点就可以作为起点了)。接下来我们证明


    如果将全部油量累计起来,总量为正,那么一定能找到一个起点,使得可以走完一圈,也就是一定有解。


    证明:
    按照我们之前的划分,整个圈会被划分成有累积量为s1, s2, ..., sk 的负序列,以及一个正序列拥有油量sp(这里正序列一定存在因为全部累加和是正的,如果全是负序列那么结果不会是正的)。而且我们知道s1+s2+...+sk+sp>0,也就是说sp>-s1-s2-...-sk。换句话说,如果我们从sp对应的站的起点出发,在sp对应的序列会一直是正的,并且,当他走到负序列时,因为sp的正油量大于所有负油量的总和,所以累加油量会一直正,完整整个圈的行驶。这证明了只要累加油量是正的,一定能找到一个起点来完成任务。
    根据上面的两个命题,我们可以来实现代码,需要维护两个量,一个是总的累积油量total,另一个是当前序列的累计油量sum,如果出现负的,则切换起点,并且将sum置0。总共是需要扫描所有站一次,时间复杂度是O(n)。而只需要两个额外变量,空间复杂度是O(1)。代码如下:

     1     // O(n)
     2     public static int canCompleteCircuit2(int[] gas, int[] cost) {
     3         int curSum = 0;            // 用于记录当前gas剩余量
     4         int total = 0;                // 记录走完一圈的gas剩余量
     5         int startIndex = 0;        // 记录能走完一圈的开始位置
     6         
     7         for(int i=0; i<gas.length; i++){
     8             int curRemain = gas[i]-cost[i];  
     9             if(curSum >= 0){        // 如果当前还有剩余量,继续
    10                 curSum += curRemain;
    11             }else{                    // 否则,从这里重新开始
    12                 curSum = curRemain;
    13                 startIndex = i;
    14             }
    15             total += curRemain;
    16         }
    17         
    18         return total>=0 ? startIndex : -1;
    19     }
  • 相关阅读:
    2020.10.23 19级training 补题报告
    2020.10.17 天梯赛练习 补题报告
    2020.10.16 19级training 补题报告
    2020.10.9 19级training 补题报告
    2020.10.10 天梯赛练习 补题报告
    2020.10.3 天梯赛练习 补题报告
    2020.10.2 19级training 补题报告
    第十届山东省ACM省赛复现补题报告
    VVDI Key Tool Plus Adds VW Passat 2015 Key via OBD
    Xhorse VVDI Prog Software V5.0.3 Adds Many MCUs
  • 原文地址:https://www.cnblogs.com/EdwardLiu/p/3961280.html
Copyright © 2011-2022 走看看