Graph · 25 of 43
Snake and Ladders Problem
Find the fewest dice throws that get you from the first square of a snakes and ladders board to the last, if you could choose every roll.
- Simple way
- time O(6^N)
room O(N) - Clever way
- time O(N)
room O(N)
The problem, simply
A snakes and ladders board has N squares, numbered 0 to N - 1. You start on square 0 and want to reach square N - 1. Each throw of the die moves you 1 to 6 squares forward. If you land at the bottom of a ladder you climb to its top. If you land on a snake's head you slide down to its tail.
You get to pick every roll. Give back the smallest number of throws to reach the last square, or -1 if you never can. You may not move past the last square.
A story to remember it
Picture it
The board winds up like a real one: row 0 runs left to right at the bottom, the next row runs right to left, and so on. Green squares are ladder bottoms, red squares are snake heads. Arrows show where each one takes you.
- looking now
- done
- answer
- rejected
Examples
The board above
- In
N = 30, ladders 2>21, 4>7, 10>25, 19>28, snakes 26>0, 20>8, 16>3, 18>6- Out
3
Roll 2 and climb to 21. Roll 2 to reach 23. Roll 6 to reach 29.
No snakes, no ladders
- In
N = 30, nothing- Out
5
You need to cover 29 squares, 6 at most per throw. Four throws cover only 24, so you need 5.
A ladder at the start
- In
N = 12, ladder 1>11- Out
1
Roll a 1 and climb straight to the last square.
Trapped
- In
N = 12, snakes 1>0, 2>0, 3>0, 4>0, 5>0, 6>0- Out
-1
Every roll from square 0 lands on a snake that sends you back to 0. You can never leave.
Way 1: the simple way
Try every sequence of rolls, like playing every possible game at once.
- From the current square, try each roll from 1 to 6. Follow any ladder or snake.
- Do not revisit a square already on this game's path, or a snake could loop forever.
- When you reach the last square, keep the smallest number of throws.
- Stop a game early when it has already used as many throws as the best so far.
class SnakesBrute {
static int best;
public static int minThrows(int n, int[] jump) { // jump[i] = where square i sends you, or -1
best = Integer.MAX_VALUE;
play(0, n, jump, 0, new boolean[n]);
return best == Integer.MAX_VALUE ? -1 : best;
}
static void play(int at, int n, int[] jump, int rolls, boolean[] onPath) {
if (at == n - 1) { best = Math.min(best, rolls); return; }
if (rolls + 1 >= best) return; // cannot beat the best game any more
onPath[at] = true;
for (int die = 1; die <= 6 && at + die < n; die++) {
int next = at + die;
int dest = jump[next] != -1 ? jump[next] : next; // climb or slide
if (!onPath[dest]) play(dest, n, jump, rolls + 1, onPath);
}
onPath[at] = false;
}
}How long it takes: up to 6 choices at every throw, and a game can wander over many squares, so it can take about O(6^N). Extra room: the path marks and the recursion, O(N).
Way 2: the clever way
Paint the board round by round. Every throw costs the same, so this is breadth first search: the first time a square is painted, it was reached with the fewest throws.
- Put square 0 in a waiting line (a queue) with 0 throws, and mark it seen.
- Take a square from the front. Try rolls 1 to 6. Follow any ladder or snake to find where you really end up.
- If that is the last square, the answer is this square's throws plus 1.
- Otherwise, if it is not seen yet, mark it and put it at the back of the line with one more throw.
- If the line runs empty, the last square cannot be reached.
The waiting line paints the board one throw at a time
01 / 08
Start on square 0 with 0 throws. It is the only square in the waiting line.
- throws
- 0
- looking now
- seen
- answer
- rejected
- path
- just added
class SnakesBFS {
public static int minThrows(int n, int[] jump) { // jump[i] = where square i sends you, or -1
if (n == 1) return 0;
boolean[] seen = new boolean[n];
int[] throwsTo = new int[n];
Queue<Integer> line = new ArrayDeque<>();
line.add(0);
seen[0] = true;
while (!line.isEmpty()) {
int at = line.poll();
for (int die = 1; die <= 6 && at + die < n; die++) {
int next = at + die;
int dest = jump[next] != -1 ? jump[next] : next; // climb or slide
if (dest == n - 1) return throwsTo[at] + 1;
if (!seen[dest]) {
seen[dest] = true; // painted once, with the fewest throws
throwsTo[dest] = throwsTo[at] + 1;
line.add(dest);
}
}
}
return -1; // the last square is never painted
}
}How long it takes: each square joins the line at most once and tries 6 rolls, O(6N), which is O(N). Extra room: the seen marks, the throw counts and the line, O(N).
Watch out for
- Snakes make loops. Without a seen mark, the search can go round and round forever.
- Rolls past the last square are not allowed. Stop the dice loop at
n - 1. - A ladder or snake that ends exactly on the last square still counts as reaching it. Check
dest, notnext. - If the start square has a ladder on it, the usual rule is that you do not climb it before your first roll.
What interviewers ask next
- LeetCode 909 gives the board as an
nbyngrid numbered in the winding order, with-1for plain squares. Turn a square number into a row and column first: the row from the bottom is(s - 1) / n, and every other row runs right to left. - Print the actual rolls. Store the square you came from for every painted square, then walk back from the end.
- What is the expected number of throws with a real random die? Then it is a Markov chain. Solve a system of equations, or do value iteration, instead of BFS.
- Two players take turns. Who wins with perfect play? That becomes a game search with memo over both positions.
Remember this
Grown-up words: squares are nodes, a roll is an edge, the rounds of painting are BFS levels, the waiting line is a queue, and the number painted on the last square is the shortest path length.