[LeetCode] 871. Minimum Number of Refueling Stops 最少的加油站個數



A car travels from a starting position to a destination which is target miles east of the starting position.html

Along the way, there are gas stations.  Each station[i] represents a gas station that is station[i][0] miles east of the starting position, and has station[i][1] liters of gas.git

The car starts with an infinite tank of gas, which initially has startFuel liters of fuel in it.  It uses 1 liter of gas per 1 mile that it drives.github

When the car reaches a gas station, it may stop and refuel, transferring all the gas from the station into the car.數組

What is the least number of refueling stops the car must make in order to reach its destination?  If it cannot reach the destination, return -1.ide

Note that if the car reaches a gas station with 0 fuel left, the car can still refuel there.  If the car reaches the destination with 0 fuel left, it is still considered to have arrived.code

Example 1:htm

Input: target = 1, startFuel = 1, stations = []
Output: 0
Explanation: We can reach the target without refueling.

Example 2:blog

Input: target = 100, startFuel = 1, stations = [[10,100]]
Output: -1
Explanation: We can't reach the target (or even the first gas station).

Example 3:leetcode

Input: target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
Output: 2
Explanation:
We start with 10 liters of fuel.
We drive to position 10, expending 10 liters of fuel.  We refuel from 0 liters to 60 liters of gas.
Then, we drive from position 10 to position 60 (expending 50 liters of fuel),
and refuel from 10 liters to 50 liters of gas.  We then drive to and reach the target.
We made 2 refueling stops along the way, so we return 2.

Note:get

  1. 1 <= target, startFuel, stations[i][1] <= 10^9
  2. 0 <= stations.length <= 500
  3. 0 < stations[0][0] < stations[1][0] < ... < stations[stations.length-1][0] < target



這道題說有一輛小車,須要向東行駛 target 的距離,路上有許多加油站,每一個加油站有兩個信息,一個是距離起點的距離,另外一個是能夠加的油量,問咱們到達 target 位置最少須要加的油量。咱們能夠從第三個例子來分析,開始時有 10 升油,能夠到達第一個加油站,此時花掉了 10 升,可是能夠補充 60 升,當前的油能夠到達其餘全部的加油站,因爲已經開了 10 邁,因此到達後面的加油站的距離分別爲 10,20,和 50。若咱們到最後一個加油站,那離起始位置就有 60 邁了,再加上此加油站提供的 40 升油,直接就能夠到達 100 位置,不用再加油了,因此總共只須要加2次油。由此能夠看出來其實咱們但願到達儘量遠的加油站的位置,同時最好該加油站中的油也比較多,這樣下一次就能到達更遠的位置。像這種求極值的問題,十有八九要用動態規劃 Dynamic Programming 來作,可是這道題的 dp 定義式並非直接來定義須要的最少加油站的個數,那樣定義的話不太好推導出狀態轉移方程。正確的定義應該是根據加油次數能到達的最遠距離,咱們就用一個一維的 dp 數組,其中 dp[i] 表示加了i次油能到達的最遠距離,那麼最後只要找第一個i值使得 dp[i] 大於等於 target 便可。dp 數組的大小初始化爲加油站的個數加1,值均初始化爲 startFuel 便可,由於初始的油量能到達的距離是肯定的。如今來推導狀態轉移方程了,遍歷每個加油站,對於每一個遍歷到的加油站k,須要再次遍歷其以前的全部的加油站i,能到達當前加油站k的條件是當前的 dp[i] 值大於等於加油站k距起點的距離,若大於等於的話,咱們能夠更新 dp[i+1] 爲 dp[i]+stations[k][1],這樣就能夠獲得最遠能到達的距離。當 dp 數組更新完成後,須要再遍歷一遍,找到第一個大於等於 target 的 dp[i] 值,並返回i便可,參見代碼以下:



解法一:

class Solution {
public:
    int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
        int n = stations.size();
        vector<long> dp(n + 1, startFuel);
        for (int k = 0; k < n; ++k) {
            for (int i = k; i >= 0 && dp[i] >= stations[k][0]; --i) {
                dp[i + 1] = max(dp[i + 1], dp[i] + stations[k][1]);
            }
        }
        for (int i = 0; i <= n; ++i) {
            if (dp[i] >= target) return i;
        }
        return -1;
    }
};



這道題還有一個標籤是 Heap,說明還能夠用堆來作,這裏是用最大堆。由於以前也分析了,咱們關心的是在最小的加油次數下能達到的最遠距離,那麼每一個加油站的油量就是關鍵因素,能夠將全部能到達的加油站根據油量的多少放入最大堆,這樣每一次都選擇油量最多的加油站去加油,才能儘量的到達最遠的地方(若是驕傲沒被現實大海冷冷拍下,又怎會懂得要多努力,才走獲得遠方。。。打住打住,要唱起來了 ^o^)。這裏須要一個變量i來記錄當前遍歷到的加油站的位置,外層循環的終止條件是 startFuel 小於 target,而後在內部也進行循環,若當前加油站的距離小於等於 startFuel,說明能夠到達,則把該加油站油量存入最大堆,這個 while 循環的做用就是把全部當前能到達的加油站的油量都加到最大堆中。這樣取出的堆頂元素就是最大的油量,也是咱們下一步須要去的地方(最想要去的地方,怎麼能在半路就返航?!),假如此時堆爲空,則直接返回 -1,表示沒法到達 target。不然就把堆頂元素加到 startFuel 上,此時的startFuel 就表示當前能到的最遠距離,是否是跟上面的 DP 解法核心思想很相似。因爲每次只能去一個加油站,此時結果 res 也自增1,當 startFuel 到達 target 時,結果 res 就是最小的加油次數,參見代碼以下:



解法二:

class Solution {
public:
    int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
        int res = 0, i = 0, n = stations.size();
        priority_queue<int> pq;
        for (; startFuel < target; ++res) {
            while (i < n && stations[i][0] <= startFuel) {
                pq.push(stations[i++][1]);
            }
            if (pq.empty()) return -1;
            startFuel += pq.top(); pq.pop();
        }
        return res;
    }
};



Github 同步地址:

https://github.com/grandyang/leetcode/issues/871



參考資料:

https://leetcode.com/problems/minimum-number-of-refueling-stops/

https://leetcode.com/problems/minimum-number-of-refueling-stops/discuss/149839/DP-O(N2)-and-Priority-Queue-O(NlogN)

https://leetcode.com/problems/minimum-number-of-refueling-stops/discuss/149858/Simple-Java-Solution-Using-PriorityQueue-O(nlogn)



LeetCode All in One 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索