The Maze III

This page explains Java solution to problem The Maze III using Breadth First Search algorithm.

Problem Statement

There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up (u), down (d), left (l) or right (r), but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. There is also a hole in this maze. The ball will drop into the hole if it rolls on to the hole.

Given the ball position, the hole position and the maze, find out how the ball could drop into the hole by moving the shortest distance. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the hole (included). Output the moving directions by using 'u', 'd', 'l' and 'r'. Since there could be several different shortest ways, you should output the lexicographically smallest way. If the ball cannot reach the hole, output "impossible".

The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The ball and the hole coordinates are represented by row and column indexes.

Example 1:

Input:
maze = [
      0 0 0 0 0
      1 1 0 0 1
      0 0 0 0 0
      0 1 0 0 1
      0 1 0 0 0
]
ball coordinate (rowBall, colBall) = (4, 3)
hole coordinate (rowHole, colHole) = (0, 1)
Output: lul
Explanation:
There are two shortest ways for the ball to drop into the hole.
The first way is left -> up -> left, represented by "lul".
The second way is up -> left, represented by 'ul'.
Both ways have shortest distance 6, but the first way is lexicographically smaller because 'l' < 'u'. So the output is "lul".

Solution

If you have any suggestions in below code, please create a pull request by clicking here.

package com.vc.hard;

import java.util.*;

class TheMazeIii {
    static class State {
        int x, y, distance;
        String path;
        State(int x, int y, int distance, String path) {
            this.x = x;
            this.y = y;
            this.distance = distance;
            this.path = path;
        }

        @Override
        public String toString() {
            return x+" "+y+" "+distance+" "+path;
        }
    }

    private int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
    private String[] dirValue = {"d", "r", "u", "l"};
    public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
        PriorityQueue<State> pq = new PriorityQueue<>(new Comparator<State>(){
            public int compare(State s1, State s2) {
                int cmp = Integer.compare(s1.distance, s2.distance);
                if(cmp == 0) return s1.path.compareTo(s2.path);
                else return cmp;
            }
        });

        pq.offer(new State(ball[0], ball[1], 0, ""));
        HashSet<String> visited = new HashSet<>();
        while(!pq.isEmpty()) {
            State state = pq.poll();

            if(state.x == hole[0] && state.y == hole[1]) return state.path;

            String key = state.x + " " + state.y;
            if(visited.contains(key)) continue;

            visited.add(key);
            for(int i = 0; i < dirs.length; i++) {
                State newState = move(state, maze, hole, dirs[i], dirValue[i]);
                if((newState.x == state.x && newState.y == state.y) ||
                        visited.contains(newState.x+" "+newState.y)) continue;
                pq.offer(newState);
            }
        }

        return "impossible";
    }

    private State move(State state, int[][] maze, int[] hole, int[] dir, String dirValue) {
        int xNew = state.x;
        int yNew = state.y;
        int res = 0;
        while(xNew + dir[0] >= 0 && xNew + dir[0] < maze.length &&
                yNew + dir[1] >= 0 && yNew + dir[1] < maze[0].length &&
                maze[xNew + dir[0]][yNew + dir[1]] == 0) {

            xNew = xNew + dir[0];
            yNew = yNew + dir[1];

            res++;
            if(xNew == hole[0] && yNew == hole[1]) break;
        }
        return new State(xNew, yNew, state.distance + res, state.path + dirValue);
    }
}

Time Complexity

O(NM * log(NM)) Where
N is total number of rows in an input array maze
M is total number of cols in an input array maze

Space Complexity

O((MN)2)
N is total number of rows in an input array maze
M is total number of cols in an input array maze