加油站問題(貪心演算法)

嘍耶發表於2020-11-23

在一條環路上有 N 個加油站,其中第 i 個加油站有汽油 gas[i] 升。

你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i+1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發,開始時油箱為空。

如果你可以繞環路行駛一週,則返回出發時加油站的編號,否則返回 -1。

說明:

  • 如果題目有解,該答案即為唯一答案。
  • 輸入陣列均為非空陣列,且長度相同。
  • 輸入陣列中的元素均為非負數。

示例 1:

輸入: 
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

輸出: 3

解釋:3 號加油站(索引為 3)出發,可獲得 4 升汽油。此時油箱有 = 0 + 4 = 4 升汽油
開往 4 號加油站,此時油箱有 4 - 1 + 5 = 8 升汽油
開往 0 號加油站,此時油箱有 8 - 2 + 1 = 7 升汽油
開往 1 號加油站,此時油箱有 7 - 3 + 2 = 6 升汽油
開往 2 號加油站,此時油箱有 6 - 4 + 3 = 5 升汽油
開往 3 號加油站,你需要消耗 5 升汽油,正好足夠你返回到 3 號加油站。
因此,3 可為起始索引。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/gas-station
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

示例 2:

輸入: 
gas  = [2,3,4]
cost = [3,4,3]

輸出: -1

解釋:
你不能從 0 號或 1 號加油站出發,因為沒有足夠的汽油可以讓你行駛到下一個加油站。
我們從 2 號加油站出發,可以獲得 4 升汽油。 此時油箱有 = 0 + 4 = 4 升汽油
開往 0 號加油站,此時油箱有 4 - 3 + 2 = 3 升汽油
開往 1 號加油站,此時油箱有 3 - 3 + 3 = 3 升汽油
你無法返回 2 號加油站,因為返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,無論怎樣,你都不可能繞環路行駛一週。

一次遍歷

從頭到尾遍歷每個加油站,並檢查以該加油站為起點,最終能否行駛一週。
我們可以通過減小被檢查的加油站數目,來降低總的時間複雜度。

假設從加油站 x 出發,每經過一個加油站就加一次油(包括起始加油站),最後一個可以到達的加油站是 y(不妨設 x<y)。這就說明:
在這裡插入圖片描述

第一個式子表明無法到達加油站 y 的下一個加油站,第二個式子表明可以到達 y以及 y之前的所有加油站。

現在,考慮任意一個位於 x,y之間的加油站 z(包括 x和 y),我們現在考察從該加油站出發,能否到達加油站 y的下一個加油站,也就是要判斷在這裡插入圖片描述之間的大小關係。

根據上面的式子,我們得到:

在這裡插入圖片描述

其中不等式的第二步、第三步分別利用了上面的第一個、第二個不等式。

從上面的推導中,能夠得出結論:從 x,y 之間的任何一個加油站出發,都無法到達加油站 y 的下一個加油站。

在發現了這一個性質後,演算法就很清楚了:我們首先檢查第 0個加油站,並試圖判斷能否環繞一週;如果不能,就從第一個無法到達的加油站開始繼續檢查。
複雜度分析
時間複雜度:O(N),其中 N 為陣列的長度。我們對陣列進行了單次遍歷。

空間複雜度:O(1)。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n=gas.length;
        int i=0;
        //從頭到尾遍歷每個加油站,並檢查以該加油站為起點,能否行駛一週
        while(i<n){
            int sumOfGas=0;				//總共加的油
            int sumOfCost=0;			//總共消費的油
            int count=0;				//走過幾個站點
            while(count<n){				//迴圈終止條件走過所有站點    
               int j=(i+count)%n;		//加油站環形
               sumOfGas+=gas[j];
               sumOfCost+=cost[j];
               if(sumOfCost>sumOfGas){	//若該站點油不夠終止迴圈
                   break;         
               }
               count++;					//站點滿足計數                                                              
            }
            if(count==n){				//環繞一週
                return i;
            }else{						//不行從下一個站點開始檢查
                i=i+count+1;
            }
        }
        return -1;					//所有站點都不滿足
    }
}

相關文章