Minimum Number of Refueling Stops

HardGreedy · Heap (Priority Queue) · DP🧩 GreedyLeetCode #871 ↗Updated

Problem

A car drives east from position 0 towards target, starting with startFuel litres and burning one litre per mile. Along the way, stations[i] = [position, fuel] (sorted by position) each offer a fixed amount of fuel; stopping takes all of it. Return the fewest stops needed to reach target, or -1 if it cannot be reached. Arriving with exactly 0 fuel still counts as arriving.
Inputtarget = 1, startFuel = 1, stations = []Output0
Inputtarget = 100, startFuel = 1, stations = [[10,100]]Output-1
Inputtarget = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]Output2

1 ≤ target, startFuel ≤ 109  ·  0 ≤ stations.length ≤ 500  ·  1 ≤ positioni < positioni+1 < target  ·  1 ≤ fueli < 109

Try it yourself

Java
class Solution {
    public int minRefuelStops(int target, int startFuel, int[][] stations) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
You don't have to decide at a station whether to stop. Drive as far as you can, remembering every station you passed; when you run dry, pretend you stopped at the richest one you skipped. A max-heap makes that “retroactive” choice cheap.
PatternGreedy

Instinct An optimisation where one obvious local rule (earliest end, farthest reach, biggest first) never needs undoing - commit to it, then try hard to break it.

Memory hack Grab the best bite now and never look back - but only once a counterexample hunt fails to make you regret it.

Open the Greedy guide →

Approaches

Brute ForceTry Every Stop / Skip Combination

Time O(2n)Space O(n)

Since fuel left = (start fuel + fuel collected) − position, the state is just the farthest reachable point. At each reachable station branch on “stop” or “skip” and keep the fewest stops among branches that reach target.

Java
public int minRefuelStops(int target, int startFuel, int[][] stations) {
    int best = explore(target, stations, 0, startFuel, 0);
    return best == Integer.MAX_VALUE ? -1 : best;
}

// reach = farthest point the fuel collected so far can carry us
private int explore(int target, int[][] stations, int i, long reach, int stops) {
    if (reach >= target) return stops;
    if (i == stations.length || stations[i][0] > reach) return Integer.MAX_VALUE;
    int stop = explore(target, stations, i + 1, reach + stations[i][1], stops + 1);
    int skip = explore(target, stations, i + 1, reach, stops);
    return Math.min(stop, skip);
}

Obviously correct, but exponential — only for tiny inputs.

At a glance

LevelApproachTimeSpace
Brute ForceTry Every Stop / Skip CombinationO(2n)O(n)
BetterDP — Farthest Reach with k StopsO(n2)O(n)
OptimalGreedy + Max-Heap of Passed StationsO(n log n)O(n)
Common pitfalls
  • Greedily stopping at every station you reach — wastes stops.
  • Running the DP inner loop upward — reuses the same station twice.
  • Using int for total reach — overflows with large fuel values.
Interview tips
  • Reframe state as “farthest reachable point” — fuel left is implied.
  • Present the DP first, then argue why the biggest skipped station is always a safe pick.
  • Mention the same “regret with a heap” trick in Course Schedule III.
Pattern
Greedy with regret (heap) → when you can postpone a choice and later grab the best option already passed.
My notes saved in this browser