Skip to content
dsa450for five year olds

Dynamic Programming · 29 of 60

Minimum number of jumps to reach end

Hop from the first square to the last in as few jumps as possible, when each square tells you how far you may jump.

Simple way
time O(2^n)
room O(n)
Clever way
time O(n)
room O(1)

The problem, simply

You get a list of non-negative numbers. You stand on square 0. The number on a square is the longest jump you may make from it: from a square with 3 you may jump 1, 2 or 3 squares forward.

Give back the fewest jumps needed to land on the last square. If you can never get there, give back -1. If the list has one square, you are already there: 0 jumps.

A story to remember it

Picture it

arraymax jump from here
01234567891013589267689

Each square says how far you may jump from it, at most. Jumping 0 to 1, then 1 to 4, then 4 to 10 reaches the end in 3 jumps.

  • answer

Examples

A normal case

In
[1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9]
Out
3

0 to 1 (the only choice), 1 to 4 (the 9 is the best launch pad), 4 to 10.

Short

In
[2, 3, 1, 1, 4]
Out
2

Jump 1 square to the 3, then 3 squares to the end. Jumping 2 squares to the 1 first would need 3 jumps.

Stuck on a zero

In
[1, 0, 3]
Out
-1

You reach the 0 and cannot move. The end is out of reach.

Already there

In
[0]
Out
0

One square means you start on the end. No jumps needed, even though the number is 0.

Way 1: the simple way

From the square you stand on, try every allowed jump length and ask the same question from where you land.

  1. If you stand on the last square, 0 more jumps.
  2. Otherwise, for each jump k from 1 to a[i], ask fewest(i + k).
  3. The answer is 1 plus the smallest of those that are possible.
tree
+1+1+1+1+2+1+3+2+1+1from 0from 1from 2from 3from 4from 3from 4from 4from 2from 3from 4

For [2, 3, 1, 1, 4]: from square i, try every jump length. Gray circles repeat work: from 3 is solved three times and from 2 twice. Square 4 is the end.

  • seen
  • done
java
class Solution {
    public int minJumps(int[] a) {
        int best = fewest(a, 0);
        return best == Integer.MAX_VALUE ? -1 : best;
    }

    private int fewest(int[] a, int i) {
        if (i >= a.length - 1) return 0;                   // on the last square
        int best = Integer.MAX_VALUE;                      // means "cannot reach"
        for (int k = 1; k <= a[i] && i + k < a.length; k++) {
            int rest = fewest(a, i + k);
            if (rest != Integer.MAX_VALUE) best = Math.min(best, 1 + rest);
        }
        return best;
    }
}

How long it takes: the same squares are solved again and again, up to O(2^n) calls. Extra room: the waiting calls, O(n).

Way 2: the clever way

Count the ripple rings.

  1. end is the last square the current ring covers. far is the farthest any square seen so far can reach. jumps counts rings.
  2. Walk i from 0 to n - 2. Update far = max(far, i + a[i]).
  3. When i reaches end, the ring is used up, so we must jump: jumps++ and end = far. If far did not move past i, we are stuck: return -1.
  4. Stop as soon as end covers the last square.

The bracket is the current ring. far points to the edge of the next ring.

Grow the ripple one ring at a time

01 / 07

arraymax jump from here
012345678910reachable with 0 jumps13589267689i

With 0 jumps we can only be on square 0, so the first ring is that one square.

jumps
0
end
0
farthest
0
  • looking now
  • seen
  • answer
java
class Solution {
    public int minJumps(int[] a) {
        int n = a.length;
        if (n <= 1) return 0;                    // already on the last square
        int jumps = 0, end = 0, far = 0;
        for (int i = 0; i < n - 1; i++) {
            far = Math.max(far, i + a[i]);       // farthest the next ring reaches
            if (i == end) {                      // the current ring is used up
                if (far <= i) return -1;         // nobody in this ring moves us forward
                jumps++;
                end = far;
                if (end >= n - 1) return jumps;  // the new ring covers the end
            }
        }
        return -1;
    }
}

How long it takes: one walk, O(n). Extra room: three numbers, O(1).

Another way: the DP table

dp[i] is the fewest jumps to land on square i. dp[0] = 0, and dp[i] is 1 plus the smallest dp[j] over squares j < i that can reach i (j + a[j] >= i).

gridfewest jumps to reach
0 (2)1 (3)2 (1)3 (1)4 (4)01122

dp for [2, 3, 1, 1, 4]. Each box is the fewest jumps to land there. Arrows come from every square that can reach it. The last box, 2, is the answer.

  • answer
java
import java.util.Arrays;

class Solution {
    public int minJumps(int[] a) {
        int n = a.length;
        int[] dp = new int[n];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] != Integer.MAX_VALUE && j + a[j] >= i) {   // j can reach i
                    dp[i] = Math.min(dp[i], dp[j] + 1);
                }
            }
        }
        return dp[n - 1] == Integer.MAX_VALUE ? -1 : dp[n - 1];
    }
}

This is O(n^2) time and O(n) room. It is slower, but it is the one to reach for when jumps have different costs, because then the rings trick no longer works.

Watch out for

  • Loop only to n - 2. If you also process the last square when i == end, you count one extra jump.
  • A single square needs 0 jumps, even if its number is 0.
  • In the recursion, adding 1 to Integer.MAX_VALUE overflows. Check before adding.

What interviewers ask next

  • Can you only ask if the end is reachable (Jump Game I)? Keep far, and fail if some i > far.
  • Print the jump path. While scanning a ring, remember which square gave the new far. Those squares are the launch pads.
  • What if you can jump forward or backward by exactly a[i] (Jump Game III)? It becomes a graph search: breadth first search from the start.
  • Why is the greedy right? It is breadth first search on squares, where each ring is one level. Breadth first search always finds the fewest steps.

Remember this

Grown-up words: the rings are BFS levels, the farthest reach is the frontier of the next level, the table version is a 1D DP over positions, and the repeated fewest(i) calls are overlapping subproblems.