Before attempting this problem, you should be comfortable with:
Unlike typical grid problems where movement is one cell at a time, the ball in this maze rolls continuously in one direction until it hits a wall. This means the ball can only stop at specific positions: cells adjacent to walls.
We can model this as a graph where each stopping position is a node, and edges connect positions reachable by rolling in any of the four directions. DFS explores all reachable stopping positions to determine if the destination is among them.
dfs function:false.true.dfs from this stopping position.true if any direction leads to the destination.dfs from the initial position and return the result.class Solution:
def dfs(self, m, n, maze, curr, destination, visit):
if visit[curr[0]][curr[1]]:
return False
if curr[0] == destination[0] and curr[1] == destination[1]:
return True
visit[curr[0]][curr[1]] = True
dirX = [0, 1, 0, -1]
dirY = [-1, 0, 1, 0]
for i in range(4):
r = curr[0]
c = curr[1]
# Move the ball in the chosen direction until it can.
while r >= 0 and r < m and c >= 0 and c < n and maze[r][c] == 0:
r += dirX[i]
c += dirY[i]
# Revert the last move to get the cell to which the ball rolls.
if self.dfs(m, n, maze, [r - dirX[i], c - dirY[i]], destination, visit):
return True
return False
def hasPath(self, maze: List[List[int]], start: List[int], destination: List[int]) -> bool:
m = len(maze)
n = len(maze[0])
visit = [[False] * n for _ in range(m)]
return self.dfs(m, n, maze, start, destination, visit)Where and are the number of rows and columns in
maze.
BFS offers an alternative traversal strategy that explores all stopping positions at each "distance level" before moving deeper. While DFS might go deep into one path before backtracking, BFS systematically explores positions in order of how many rolls it takes to reach them.
The core rolling mechanic remains the same: the ball rolls until hitting a wall, and we only consider positions where the ball actually stops.
true.false.class Solution:
def hasPath(self, maze: List[List[int]], start: List[int], destination: List[int]) -> bool:
m = len(maze)
n = len(maze[0])
visit = [[False] * n for _ in range(m)]
queue = deque()
queue.append(start)
visit[start[0]][start[1]] = True
dirX = [0, 1, 0, -1]
dirY = [-1, 0, 1, 0]
while queue:
curr = queue.popleft()
if curr[0] == destination[0] and curr[1] == destination[1]:
return True
for i in range(4):
r = curr[0]
c = curr[1]
# Move the ball in the chosen direction until it can.
while r >= 0 and r < m and c >= 0 and c < n and maze[r][c] == 0:
r += dirX[i]
c += dirY[i]
# Revert the last move to get the cell to which the ball rolls.
r -= dirX[i]
c -= dirY[i]
if not visit[r][c]:
queue.append([r, c])
visit[r][c] = True
return FalseWhere and are the number of rows and columns in
maze.
In typical grid problems, movement is one cell at a time in any direction. Here, the ball rolls continuously until hitting a wall. A common mistake is moving only one cell per step or checking neighbors adjacently. The ball can only stop at positions next to walls, not at arbitrary empty cells.
The ball can only stop when it hits a wall, not mid-roll. Even if the destination is on the rolling path, the ball cannot stop there unless a wall forces it to. Check if the current position equals the destination only after the ball has stopped rolling.
The while loop condition checks the next cell before moving, but after exiting, the ball is one step past a valid position (inside a wall or out of bounds). Always subtract the direction vector after the loop to get the actual stopping cell.