Before attempting this problem, you should be comfortable with:
Unlike Maze I where we just need to determine reachability, here we need to find the shortest distance. The ball rolls until it hits a wall, and each cell it passes counts as one unit of distance.
We can use DFS with distance tracking: maintain a distance matrix where each cell stores the shortest known distance to reach it. When we find a shorter path to a stopping position, update the distance and continue exploring from there.
0.dfs function:dfs from the initial position.-1 if it remains infinity.class Solution {
public int shortestDistance(int[][] maze, int[] start, int[] dest) {
int[][] distance = new int[maze.length][maze[0].length];
for (int[] row: distance)
Arrays.fill(row, Integer.MAX_VALUE);
distance[start[0]][start[1]] = 0;
dfs(maze, start, distance);
return distance[dest[0]][dest[1]] == Integer.MAX_VALUE ? -1 : distance[dest[0]][dest[1]];
}
public void dfs(int[][] maze, int[] start, int[][] distance) {
int[][] dirs={{0,1}, {0,-1}, {-1,0}, {1,0}};
for (int[] dir: dirs) {
int x = start[0] + dir[0];
int y = start[1] + dir[1];
int count = 0;
while (x >= 0 && y >= 0 && x < maze.length && y < maze[0].length && maze[x][y] == 0) {
x += dir[0];
y += dir[1];
count++;
}
if (distance[start[0]][start[1]] + count < distance[x - dir[0]][y - dir[1]]) {
distance[x - dir[0]][y - dir[1]] = distance[start[0]][start[1]] + count;
dfs(maze, new int[]{x - dir[0],y - dir[1]}, distance);
}
}
}
}Where and are the number of rows and columns in
maze.
BFS can also solve this problem, but unlike typical BFS for shortest path, we cannot stop when we first reach the destination. This is because the ball rolls varying distances, so the first path to reach a position might not be the shortest.
Instead, we use BFS with distance relaxation: whenever we find a shorter path to a stopping position, we update the distance and add it back to the queue for further exploration.
0.-1 if unreachable.class Solution {
public int shortestDistance(int[][] maze, int[] start, int[] dest) {
int[][] distance = new int[maze.length][maze[0].length];
for (int[] row: distance)
Arrays.fill(row, Integer.MAX_VALUE);
distance[start[0]][start[1]] = 0;
int[][] dirs={{0, 1} ,{0, -1}, {-1, 0}, {1, 0}};
Queue < int[] > queue = new LinkedList < > ();
queue.add(start);
while (!queue.isEmpty()) {
int[] s = queue.remove();
for (int[] dir: dirs) {
int x = s[0] + dir[0];
int y = s[1] + dir[1];
int count = 0;
while (x >= 0 && y >= 0 && x < maze.length && y < maze[0].length && maze[x][y] == 0) {
x += dir[0];
y += dir[1];
count++;
}
if (distance[s[0]][s[1]] + count < distance[x - dir[0]][y - dir[1]]) {
distance[x - dir[0]][y - dir[1]] = distance[s[0]][s[1]] + count;
queue.add(new int[] {x - dir[0], y - dir[1]});
}
}
}
return distance[dest[0]][dest[1]] == Integer.MAX_VALUE ? -1 : distance[dest[0]][dest[1]];
}
}Where and are the number of rows and columns in
maze.
Since edge weights (rolling distances) vary, Dijkstra's algorithm is a natural fit. The key insight is that once we process a position with Dijkstra (after extracting it with the minimum distance), we have found the shortest path to that position.
This version uses a simple implementation where we scan the entire distance matrix to find the unvisited position with minimum distance. While correct, this approach is slower than using a priority queue.
false. Set start distance to 0.-1 if unreachable.class Solution {
public int shortestDistance(int[][] maze, int[] start, int[] dest) {
int[][] distance = new int[maze.length][maze[0].length];
boolean[][] visited = new boolean[maze.length][maze[0].length];
for (int[] row: distance)
Arrays.fill(row, Integer.MAX_VALUE);
distance[start[0]][start[1]] = 0;
dijkstra(maze, distance, visited);
return distance[dest[0]][dest[1]] == Integer.MAX_VALUE ? -1 : distance[dest[0]][dest[1]];
}
public int[] minDistance(int[][] distance, boolean[][] visited) {
int[] min={-1,-1};
int min_val = Integer.MAX_VALUE;
for (int i = 0; i < distance.length; i++) {
for (int j = 0; j < distance[0].length; j++) {
if (!visited[i][j] && distance[i][j] < min_val) {
min = new int[] {i, j};
min_val = distance[i][j];
}
}
}
return min;
}
public void dijkstra(int[][] maze, int[][] distance, boolean[][] visited) {
int[][] dirs={{0,1},{0,-1},{-1,0},{1,0}};
while (true) {
int[] s = minDistance(distance, visited);
if (s[0] < 0)
break;
visited[s[0]][s[1]] = true;
for (int[] dir: dirs) {
int x = s[0] + dir[0];
int y = s[1] + dir[1];
int count = 0;
while (x >= 0 && y >= 0 && x < maze.length && y < maze[0].length && maze[x][y] == 0) {
x += dir[0];
y += dir[1];
count++;
}
if (distance[s[0]][s[1]] + count < distance[x - dir[0]][y - dir[1]]) {
distance[x - dir[0]][y - dir[1]] = distance[s[0]][s[1]] + count;
}
}
}
}
}Where and are the number of rows and columns in
maze.
Using a priority queue (min-heap) optimizes Dijkstra's algorithm by efficiently extracting the position with the minimum distance. Instead of scanning the entire matrix, we simply pop from the heap.
When we pop a position from the heap, if its distance is already worse than what we have recorded, we skip it (this handles duplicate entries). Otherwise, we explore all four directions and add newly discovered shorter paths to the heap.
0.-1 if unreachable.class Solution:
def shortestDistance(self, maze: List[List[int]], start: List[int], destination: List[int]) -> int:
distance = [[float('inf')] * len(maze[0]) for _ in range(len(maze))]
distance[start[0]][start[1]] = 0
self.dijkstra(maze, start, distance)
return -1 if distance[destination[0]][destination[1]] == float('inf') else distance[destination[0]][destination[1]]
def dijkstra(self, maze: List[List[int]], start: List[int], distance: List[List[int]]) -> None:
dirs = [[0, 1], [0, -1], [-1, 0], [1, 0]]
queue = []
heapq.heappush(queue, (0, start[0], start[1])) # (distance, x, y)
while queue:
dist, sx, sy = heapq.heappop(queue)
if distance[sx][sy] < dist:
continue
for dx, dy in dirs:
x, y = sx + dx, sy + dy
count = 0
while 0 <= x < len(maze) and 0 <= y < len(maze[0]) and maze[x][y] == 0:
x += dx
y += dy
count += 1
if distance[sx][sy] + count < distance[x - dx][y - dy]:
distance[x - dx][y - dy] = distance[sx][sy] + count
heapq.heappush(queue, (distance[x - dx][y - dy], x - dx, y - dy))Where and are the number of rows and columns in
maze.
Unlike standard BFS where the first path to a node is the shortest, the ball can reach the same position via paths of different lengths due to variable rolling distances. You must continue exploring until all shorter paths are exhausted. The correct approach is to only finalize a distance when extracted from a priority queue or when no shorter path exists.
The ball rolls through multiple cells before stopping, and each cell traversed counts as one unit of distance. A common error is counting only the stopping position or miscounting the cells rolled. Increment the counter inside the while loop as the ball moves, not after it stops.
The rolling loop continues until the ball hits a wall or boundary, meaning the final position is invalid. You must subtract the direction vector once to get the actual stopping position. Forgetting this step causes the ball to be placed inside walls or outside the maze.