You are given an n x n
integer matrix board
where the cells are labeled from 1
to n2
in a Boustrophedon style starting from the bottom left of the board (i.e. board[n - 1][0]
) and alternating direction each row.
You start on square 1
of the board. In each move, starting from square curr
, do the following:
next
with a label in the range [curr + 1, min(curr + 6, n2)]
.
next
has a snake or ladder, you must move to the destination of that snake or ladder. Otherwise, you move to next
.n2
.A board square on row r
and column c
has a snake or ladder if board[r][c] != -1
. The destination of that snake or ladder is board[r][c]
. Squares 1
and n2
are not the starting points of any snake or ladder.
Note that you only take a snake or ladder at most once per dice roll. If the destination to a snake or ladder is the start of another snake or ladder, you do not follow the subsequent snake or ladder.
[[-1,4],[-1,3]]
, and on the first move, your destination square is 2
. You follow the ladder to square 3
, but do not follow the subsequent ladder to 4
.Return the least number of dice rolls required to reach the square n2
. If it is not possible to reach the square, return -1
.
Example 1:
Input: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]] Output: 4 Explanation: In the beginning, you start at square 1 (at row 5, column 0). You decide to move to square 2 and must take the ladder to square 15. You then decide to move to square 17 and must take the snake to square 13. You then decide to move to square 14 and must take the ladder to square 35. You then decide to move to square 36, ending the game. This is the lowest possible number of moves to reach the last square, so return 4.
Example 2:
Input: board = [[-1,-1],[-1,3]] Output: 1
Constraints:
n == board.length == board[i].length
2 <= n <= 20
board[i][j]
is either -1
or in the range [1, n2]
.1
and n2
are not the starting points of any snake or ladder.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to Snakes and Ladders involves exploring every possible path a player can take on the board. We will simulate every possible sequence of dice rolls and moves until we find the shortest path to the end. It's like trying out every single option to guarantee finding the solution.
Here's how the algorithm would work step-by-step:
def snakes_and_ladders_brute_force(board, number_of_rows, number_of_columns):
board_size = number_of_rows * number_of_columns
queue = [(1, 0)]
visited = {1}
while queue:
current_square, moves = queue.pop(0)
if current_square == board_size:
return moves
# Consider all possible dice rolls (1 through 6).
for dice_roll in range(1, 7):
next_square = current_square + dice_roll
if next_square <= board_size:
# Account for snakes and ladders
row = (next_square - 1) // number_of_columns
col = (next_square - 1) % number_of_columns
if number_of_rows % 2 == 0:
if row % 2 == 0:
board_index = next_square - 1
else:
board_index = (row + 1) * number_of_columns - col - 1
else:
if row % 2 == 0:
board_index = (row + 1) * number_of_columns - col - 1
else:
board_index = next_square - 1
if board[number_of_rows - 1 - (board_index // number_of_columns)][board_index % number_of_columns] != -1:
next_square = board[number_of_rows - 1 - (board_index // number_of_columns)][board_index % number_of_columns]
# Only explore unvisited squares.
if next_square not in visited:
visited.add(next_square)
queue.append((next_square, moves + 1))
# If no path is found, return -1.
return -1
The goal is to find the shortest path to the end of the board. We can treat the board as a graph and use a common graph search algorithm to find the quickest route.
Here's how the algorithm would work step-by-step:
from collections import deque
def snakes_and_ladders(board):
board_size = len(board)
squares = board_size * board_size
def get_coordinates(square_number):
row = (square_number - 1) // board_size
col = (square_number - 1) % board_size
row = board_size - 1 - row
if (board_size % 2 == 0 and (board_size - 1 - row) % 2 != 0) or \
(board_size % 2 != 0 and (board_size - 1 - row) % 2 == 0):
col = board_size - 1 - col
return row, col
queue = deque([(1, 0)])
visited = {1}
while queue:
current_square, moves_made = queue.popleft()
if current_square == squares:
return moves_made
for dice_roll in range(1, 7):
next_square = current_square + dice_roll
if next_square > squares:
continue
row, col = get_coordinates(next_square)
# Account for snakes and ladders
if board[row][col] != -1:
next_square = board[row][col]
if next_square not in visited:
visited.add(next_square)
# Breadth-first search guarantees shortest path
queue.append((next_square, moves_made + 1))
return -1
Case | How to Handle |
---|---|
Null or empty board | Return -1 immediately as no game can be played. |
Board size of 1x1 | Return 0 immediately since the start is already the end. |
No ladders or snakes on the board | The shortest path will be the number of cells minus 1, proceed with standard BFS. |
Start and end are directly connected by a ladder or snake | The BFS algorithm will prioritize this direct connection. |
Board filled entirely with snakes leading to the start | The algorithm should correctly determine that the end cannot be reached and return -1. |
Cyclic snakes and ladders leading nowhere | The visited set prevents infinite loops, allowing the algorithm to terminate if there's no path. |
Integer overflow when calculating the next cell position | Use long data type to store cell positions before converting to row and column. |
Maximum sized board (e.g., 20x20) | The BFS will still work, but the time and memory complexity may increase depending on the distribution of snakes and ladders; analyze for potential optimizations. |