【Leetcode】871. 最低加油次数——2074

题目

871. 最低加油次数

汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。

沿途有加油站,用数组 stations 表示。其中 \(stations[i] = [position_i, fuel_i]\) 表示第 i 个加油站位于出发位置东面 \(position_i\) 英里处,并且有 \(fuel_i\) 升汽油。

假设汽车油箱的容量是无限的,其中最初有 startFuel 升燃料。它每行驶 1 英里就会用掉 1 升汽油。当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。

为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 -1

注意:如果汽车到达加油站时剩余燃料为 0,它仍然可以在那里加油。如果汽车到达目的地时剩余燃料为 0,仍然认为它已经到达目的地。

  • \(1\leq target,startFuel\leq 10^9\)
  • \(0\leq stations.length\leq 500\)
  • \(1\leq position_i < position_{i+1}< target\)
  • \(1\leq fuel_i < 10^9\)

思路

1. DP

考虑到stations的长度优先,我们就考虑将stations作为dp的对象

dp[i][j]表示走到第istation,加油j次,能够剩余的最多燃料

为什么最多燃料?因为如果能够到达这个位置,那么选择同样的加油次数的情况下,能够尽可能多的获得燃料必然是不比选择其他燃料更差。

i位置的时候,当前可以选择加油或者不加油
因此如果\(dp[i-1][j]>=pos[i]-pos[i-1]\),表示剩余的油量可以达到当前位置,那么此时可以不选择加油
\(dp[i][j] = max(dp[i][j],dp[i-1][j]-(pos[i]-pos[i-1]))\)

当前也可以选择此时继续加油\(dp[i-1][j-1]>=pos[i]-pos[i-1]\)
\(dp[i][j] = max(dp[i][j], dp[i-1][j-1]-(pos[i]-pos[i-1])+Fuel[i])\)

而初始状态是
\(dp[0][0] = 0\)
\(dp[0][1] = Fuel[0]\)

此外,可以看到的是,\(dp[i]\)只与\(dp[i-1]\)有关,因此可以只记录上一层的\(dp[i-1]\)即可。

另外,考虑到dp[i][j]只与dp[i][j-1]有关,即状态只与前序有关,那么此时就可以实现逆序原地修改了。

那么需要返回的结果是什么?
如果当前处理到的位置已经超过了target,那么就没必要继续了。

返回的结果就是当前的位置距离目的地所有可能的最小加油次数,即
min([j for j in range(n+1) if dp[j]>=target] , default=-1)

class Solution:
    def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
        dp = [0] * (n + 1) # dp[i]表示加i次油能到达的最远距离
        dp[0] = startFuel
        for i,(p,f) in enumerate(stations):
            if p>target:break
            flag = False
            for j in range(i,-1,-1):
                if dp[j]>=p:
                    flag = True
                    dp[j+1] = max(dp[j+1],dp[j]+f)
            if not flag:return -1
        return min([j for j in range(n+1) if dp[j]>=target] , default=-1)

可以看到,代码中也添加了一些额外的东西,即如果从上一个位置都到达不了当前位置,那么此时就表明一定也是到达不了target了,直接返回。

2. heap

考虑如果我们已经到达第i个加油站,此时无法到达下一个加油站了,那么此时我们可以选择当前在j个加油站进行加油。那么对于选择的这个j,选择Fuel[j]更大的总是不差于更小的,因此其能到达更远的位置。

因此我们就采用最大堆的方式,将走过位置的所有Fuel[i]进行添加,如果当前位置无法到达下一个加油站,或者目的地,那么此时就选择从堆中进行加油。

class Solution:
    def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
        n = len(stations)
        h = []
        cur = startFuel
        last = 0
        ans = 0
        for p,f in stations:
            cur -= p - last
            while h and cur < 0:
                cur += -heappop(h)
                ans+=1
            if cur < 0: return -1
            heappush(h,-f)
            last = p
        while h and cur < target - last:
            cur += -heappop(h)
            ans+=1
        return ans if cur >= target - last else -1

此外,为了精简代码,可以考虑将目的地视为一个虚拟的加油站。

class Solution:
    def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
        stations.append([target,0])
        h = []
        cur = startFuel
        ans = 0
        for p,f in stations:
            while h and cur < p:
                cur += -heappop(h)
                ans+=1
            if cur < p: return -1
            heappush(h,-f)
        return ans
posted @ 2024-10-07 17:27  TICSMC  阅读(22)  评论(0)    收藏  举报