我自己最初的想法是动态规划 f[i][j] 表示到第i个点时,加j次油最多能走多少英里
f[i][j] = max(f[k][j-1] + k点的加油量 - k点到i点的油耗量)
最后选择f[target点][i]>0中,最小的i值
from typing import List
class Solution:
def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
if len(stations) == 0:
return 0 if startFuel - target >= 0 else -1
stations.append([target, 0])
n = len(stations)
# f[i][j]表示到第i个点时,加j次油最多能走多少英里
f = [[-1000000000 for i in range(n+1)] for i in range(n+1)]
f[0][0] = startFuel
for i in range(1, n+1):
f[i][0] = startFuel - stations[i-1][0]
for i in range(1, n+1):
for j in range(1, i):
for k in range(j, i):
if f[k][j-1] >= 0:
f[i][j] = max(f[i][j], f[k][j-1] + stations[k-1][1] - (stations[i-1][0] - stations[k-1][0]))
for i in range(n):
if f[n][i] >= 0:
return i
return -1
这样能得出正确结果,但n范围是500,三维的循环提交后会超时
于是我又去看题解了
题解主要有两种方法:
① 动态规划:只需要O(n^2)的时间复杂度
其实相当于自己想到的动规方法,降维一下
dp[i]表示加i次油,最多能走到哪儿,也就是dp[i]表示使用i个加油站可以到达的最远位置。
最终求解dp[i]>=target的最小i。
以下是题解中给的代码:
class Solution(object):
def minRefuelStops(self, target, startFuel, stations):
dp = [startFuel] + [0] * len(stations)
for i, (location, capacity) in enumerate(stations):
for t in xrange(i, -1, -1):
if dp[t] >= location:
dp[t+1] = max(dp[t+1], dp[t] + capacity)
for i, d in enumerate(dp):
if d >= target: return i
return -1
② 贪心+堆
当经过加油站时,我们不需要决定是否要在这个加油站加油,而是将这个加油站的可加油量加入堆中,然后我们不断往前走,直到剩余燃料不足以到达下一个加油站时,我们从已有的堆中逆向加油,贪婪地选择最大的加油站加油即可。
这样一方面能够保证到达目的地,同时也用一种贪心法,确保了每次加油都是加了最多的油,从而不浪费加油次数,求解出最小加油次数。
如果在这个过程中,车的油量小于0,即便前面每一个加油站都停下来加油也无法到达下一个加油站或者目的地,就返回-1。
代码如下:
class Solution(object):
def minRefuelStops(self, target, tank, stations):
# A maxheap is simulated using negative values
# 由于我们要的是大顶堆,而heapq维护的是一个小顶堆,所以我们每次都把油量的负值加进堆中
pq = []
# float('inf')表示正无穷
stations.append((target, float('inf')))
ans = prev = 0
for location, capacity in stations:
tank -= location - prev
# 当堆中还有值,且目前无法到达下一个加油站或目的地时
while pq and tank < 0: # must refuel in past
tank += -heapq.heappop(pq)
ans += 1
if tank < 0: return -1
# 把当前加油站的油量的负值加入到堆中
heapq.heappush(pq, -capacity)
prev = location
return ans