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
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.
- If you stand on the last square, 0 more jumps.
- Otherwise, for each jump
kfrom 1 toa[i], askfewest(i + k). - The answer is 1 plus the smallest of those that are possible.
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
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.
endis the last square the current ring covers.faris the farthest any square seen so far can reach.jumpscounts rings.- Walk
ifrom 0 ton - 2. Updatefar = max(far, i + a[i]). - When
ireachesend, the ring is used up, so we must jump:jumps++andend = far. Iffardid not move pasti, we are stuck: return -1. - Stop as soon as
endcovers 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
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
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).
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
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 wheni == 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_VALUEoverflows. Check before adding.
What interviewers ask next
- Can you only ask if the end is reachable (Jump Game I)? Keep
far, and fail if somei > 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.