LeetCode 871. 最低加油次数
题目描述


题意分析
从位置 0 出发,每行驶一单位距离消耗一单位油,油箱容量不限。每站可以选择不停,或停一次取走全部油,要求到达终点的最少停靠次数。站点已按位置递增排列,恰好耗尽油到达站点时仍可加油,恰好到达终点也算成功。
解法:贪心 + 最大堆
核心思路
[!blue]
用
fuel表示从起点出发、使用已选择站点的油后能到达的最远坐标,而不是车在某处的剩余油量。它初始为startFuel,每选择一站就直接加上该站油量,无需逐段扣除路程。将位置<= fuel的所有未处理站点油量放入最大堆;它们都已经可达,但还没有被选为停靠站。只要
fuel < target,就至少还需要一次加油,并且新增补给必须先来自当前范围内的站点,否则根本到不了那个站。堆中任意一站都只花一次停靠,选油量最大的站能使新范围至少和其他选择一样远,也不会失去后续站点。因此将下一次较小油量选择换成堆顶不会增加所需次数,可以反复做这个贪心选择。从堆中选中较早的站点,表示补上“经过它时停靠”的决定,并不要求车辆倒退。该站本来就可达,提前携带这些油也没有容量限制;所选站点按位置顺序实际经过即可。每次扩大范围后,先把新可达站点全部加入,再决定下一次加油,避免漏掉油量更大的新候选。
到达
target后立即停止,继续加油只会多算次数。若尚未到达而堆已为空,意味着所有可达补给都已用完,更远站点也无法接近,只能返回 -1。
解题步骤
- 初始化最远可达坐标
fuel = startFuel、站点下标idx = 0、停靠次数stops = 0。- 当
fuel < target时,将从idx开始所有位置不超过fuel的站点加入最大堆,并推进下标,保证每站只入堆一次。- 堆为空则返回 -1;否则弹出最大油量,加到
fuel,并将停靠次数加 1。- 可达范围覆盖终点后返回次数。初始油量已经足够时循环不执行,返回 0;站点下标到头时,只要堆里仍有油,就应继续尝试。
代码实现
class Solution {
public int minRefuelStops(int target, int startFuel, int[][] stations) {
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
int fuel = startFuel;
int idx = 0;
int stops = 0;
while (fuel < target) {
// 先将新可达站点补入候选池,边界站点也可加油
while (idx < stations.length && stations[idx][0] <= fuel) {
maxHeap.offer(stations[idx][1]);
idx++;
}
if (maxHeap.isEmpty()) {
return -1;
}
// 选择最大油量增加最远可达位置,出堆才计一次停靠
fuel += maxHeap.poll();
stops++;
}
return stops;
}
}
import "container/heap"
type MaxHeap []int
func (h MaxHeap) Len() int { return len(h) }
func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(x any) { *h = append(*h, x.(int)) }
func (h *MaxHeap) Pop() any {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func minRefuelStops(target int, startFuel int, stations [][]int) int {
h := &MaxHeap{}
heap.Init(h)
fuel := startFuel
idx := 0
stops := 0
for fuel < target {
// 先将新可达站点补入候选池,边界站点也可加油
for idx < len(stations) && stations[idx][0] <= fuel {
heap.Push(h, stations[idx][1])
idx++
}
if h.Len() == 0 {
return -1
}
// 选择最大油量增加最远可达位置,出堆才计一次停靠
fuel += heap.Pop(h).(int)
stops++
}
return stops
}
复杂度分析
- 时间复杂度:$O(n\log(n+1)+1)$,各站至多入堆、出堆一次。
- 空间复杂度:$O(n+1)$,候选最大堆。
关键点总结
[!green]
- 变量表示最远可达坐标,不是当前位置的剩余油量。
- 入堆只登记机会,弹出才计加油次数。
易错点总结
[!yellow]
- 最小堆会优先选收益较小的停靠。
- 不包含位置等于可达边界的站,会错失刚好能到的加油机会。
- 站点指针到头就判断失败,会忽略堆里仍未使用的油。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 502. IPO | 困难 | 同样先纳入当前可达门槛的资源,再用最大堆选择能带来最大增益者。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!