871. 最低加油次数
考点
- 线性DP
- 贪心
- 优先队列
思路
1. 问题描述
给定:
- 目标位置:\(\text{target}\)
- 起始油量:\(\text{startFuel}\)
- \(n\) 个加油站,按位置升序给出 第 \(i\) 个站点位于 \(\text{pos}_i\),可提供 \(\text{fuel}_i\) 的油量
车辆从位置 \(0\) 出发,油量为 \(\text{startFuel}\),每前进 \(1\) 单位距离消耗 \(1\) 单位油。
目标:以 最少的加油次数 到达位置 \(\text{target}\);若无法到达,返回 \(-1\)。
2. 设计视角总览
该问题的关键困难在于:
“最少加油次数”并不是一个容易直接递推的量, 因为是否还能继续前进,取决于当前最远可达距离。
因此,主流解法都会把问题重构为:
在固定加油次数的条件下,最多能到达多远?
围绕这一思想,有三种等价但实现方式不同的解法:
- 二维动态规划(最直观、最易证明)
- 一维动态规划(二维 DP 的空间压缩)
- 贪心 + 最大堆(对 DP 的“在线化 / 最短路化”优化)
3. 二维动态规划(核心讲解)
3.1 状态定义
设加油站已按位置升序排列,编号为 \(1 \sim n\)。
定义状态: \[ f[i][j] = \begin{cases} \text{在只考虑前 } i \text{ 个加油站的前提下} \\ \text{恰好加油 } j \text{ 次所能到达的最远距离} \end{cases} \] 该状态的含义非常重要:
- \(i\) 控制“可用的站点集合”
- \(j\) 精确计数加油次数
- \(f[i][j]\) 将所有路径与剩余油量信息压缩为一个“最远可达边界”
3.2 初始化
- 不使用任何加油站、且不加油:
\[ f[0][0] = \text{startFuel} \]
- 其余状态均不可达(初始化为 \(0\) 或负无穷)
3.3 状态转移
考虑第 \(i\) 个加油站(位置 \(\text{pos}_i\),油量 \(\text{fuel}_i\))。
对每个 \(j = 0 \sim i\),有两种选择:
情况一:不在第 \(i\) 个站加油
\[ f[i][j] = f[i-1][j] \]
情况二:在第 \(i\) 个站加油一次
前提是:必须能到达该站点 \[ f[i-1][j-1] \ge \text{pos}_i \] 若满足,则: \[ f[i][j] = \max\left( f[i][j],\; f[i-1][j-1] + \text{fuel}_i \right) \]
3.4 答案提取
当所有站点处理完毕后,在最后一行中寻找最小的 \(j\) 使得: \[ f[n][j] \ge \text{target} \] 该 \(j\) 即为最少加油次数;若不存在,则返回 \(-1\)。
4. 一维动态规划(空间压缩)
4.1 压缩思想
由于状态转移只依赖于上一行 \(f[i-1][\cdot]\), 可以将二维数组压缩为一维数组: \[ f[j] = \text{当前阶段下,加油 } j \text{ 次的最远可达距离} \]
4.2 关键实现要点
- 必须从大到小枚举 \(j\)
- 防止同一加油站在同一轮中被使用多次(0/1 背包经典要求)
5. 贪心 + 最大堆
5.1 核心思想
将问题理解为:
在向前推进的过程中, 当“当前可达距离不足以到达下一个站点”时, 必须增加一次加油操作。
而既然必须加油:
应当优先选择此前经过的站点中,油量最大的那一次加油。
这是一种“延迟决策”的贪心策略。
5.2 算法流程
- 将终点 \(\text{target}\) 视作一个油量为 \(0\) 的虚拟站点
- 维护:
- 当前最远可达距离
fuel - 一个最大堆,保存“已经经过但尚未使用的站点油量”
- 当前最远可达距离
- 顺序扫描站点:
- 若
fuel < pos,则从堆中取最大油量补充(加油次数 +1) - 若堆空仍无法到达,则返回 \(-1\)
- 将当前站点的油量加入堆
- 若
6. 参考实现(带注释)
6.1 二维 DP(AC)
1 | class Solution { |
6.2 一维 DP(空间压缩)
1 | class Solution { |
6.3 贪心 + 最大堆(AC)
1 | class Solution { |