【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]表示走到第i个station,加油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

浙公网安备 33010602011771号