GreenSkies
GreenSkies

Reputation: 151

Binary Maze with a Trick

The problem is a binary maze where 1's are walls and 0's are valid paths. You start from the top left (0,0) and you have to reach the bottom right (width -1, height -1). You have to find the shortest path from the top left to the bottom right. The twist comes in because you are allowed to remove one wall at most and this is the part that is confusing me. My current code can solve the shortest path without calculating for a wall being removed.

Here are a couple of examples: [0,1,1,0], [0,0,0,1], [1,1,0,0], [1,1,1,0] Answer: 7 moves (including entrance and exit)

Example 2: [0,0,0,0,0],[0,1,1,1,0],[1,0,0,0,0],[0,1,1,1,1],[0,1,1,1,1],[0,0,0,0,0] Answer: 11 (Because you can break the wall at position [0][1] to make the path shorter)

So like I said before my code will find the shortest path, but at the moment doesn't try to remove a wall for a shorter path, mostly because I dont understand how to. I was thinking that I remove one wall at a time and continually re-run to see if a shorter path is produced but this seems very costly operation, but it might get the job done. However, I was hoping that I could find a more clear and easier way to do so.

import java.util.*;

    public class Maze { 
        public static void main(String [] args)
        {
            int[][] arr = new int[][] {
                {0,0,0,0,0},
                {1,1,1,1,0},
                {0,0,0,0,0},
                {0,1,1,1,1},
                {0,1,1,1,1},
                {0,0,0,0,0},
            };
            answer(arr);
        }
        public static int answer(int[][] maze) { 
            maze[maze.length-1][maze[0].length -1] = 9;
            Point p = getPathBFS(0,0,maze);
            int length = 1;

            while(p.getParent() != null) {
                p = p.getParent();
                length++;
            }
            System.out.println(length);
            return length;
        } 
         private static class Point {
            int x;
            int y;
            Point parent;

            public Point(int x, int y, Point parent) {
                this.x = x;
                this.y = y;
                this.parent = parent;
            }

            public Point getParent() {
                return this.parent;
            }
      }
      public static Queue<Point> q = new LinkedList<Point>();

        public static Point getPathBFS(int x, int y,int[][] arr) {

            q.add(new Point(x,y, null));

            while(!q.isEmpty()) {
                Point p = q.remove();

                if (arr[p.x][p.y] == 9) {
                    return p;
                }

                if(isFree(p.x+1,p.y,arr)) {
                    arr[p.x][p.y] = -1;
                    Point nextP = new Point(p.x+1,p.y, p);
                    q.add(nextP);
                }

                if(isFree(p.x-1,p.y,arr)) {
                    arr[p.x][p.y] = -1;
                    Point nextP = new Point(p.x-1,p.y, p);
                    q.add(nextP);
                }

                if(isFree(p.x,p.y+1,arr)) {
                    arr[p.x][p.y] = -1;
                    Point nextP = new Point(p.x,p.y+1, p);
                    q.add(nextP);
                }

                 if(isFree(p.x,p.y-1,arr)) {
                    arr[p.x][p.y] = -1;
                    Point nextP = new Point(p.x,p.y-1, p);
                    q.add(nextP);
                }

            }
            return null;
        }


        public static boolean isFree(int x, int y,int[][] arr) {
            if((x >= 0 && x < arr.length) && (y >= 0 && y < arr[x].length) && (arr[x][y] == 0 || arr[x][y] == 9)) {
                return true;
            }
            return false;
        }
    }

Upvotes: 2

Views: 2549

Answers (2)

Matt Timmermans
Matt Timmermans

Reputation: 59194

You use a Point class to represent the position of the player as he walks through the maze. Without the wall rule, this position is all you need to know about the player to determine what he can do next, and so you can do BFS on the directed graph of all possible positions to find the path.

With the wall rule, in order to determine where the player can go next, you also have to know whether or not he has already removed a wall, so the full state of the player includes not only his position, but also a boolean that indicates whether or not a wall has been removed.

You can then do BFS on the graph of these expanded states to find the shortest path with at most one wall removed.

Since you've posted your actual code for the simpler problem, I'll just fix it up with the expanded state (and a Set to keep you from visiting the same state twice, instead of modifying arr):

import java.util.*;

    public class Maze { 
        public static void main(String [] args)
        {
            int[][] arr = new int[][] {
                {0,0,0,0,0},
                {1,1,1,1,0},
                {0,0,0,0,0},
                {0,1,1,1,1},
                {0,1,1,1,1},
                {0,0,0,0,0},
            };
            answer(arr);
        }
        public static int answer(int[][] maze) { 
            maze[maze.length-1][maze[0].length -1] = 9;
            State p = getPathBFS(0,0,maze);
            int length = 1;

            while(p.getParent() != null) {
                p = p.getParent();
                length++;
            }
            System.out.println(length);
            return length;
        } 
         private static class State {
            int x;
            int y;
            boolean wallRemoved;

            State parent;

            public State(int x, int y, boolean wallRemoved, State parent) {
                this.x = x;
                this.y = y;
                this.wallRemoved = wallRemoved;
                this.parent = parent;
            }

            public State getParent() {
                return this.parent;
            }

            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + (wallRemoved ? 1231 : 1237);
                result = prime * result + x;
                result = prime * result + y;
                return result;
            }

            @Override
            public boolean equals(Object obj)  {
                if (this == obj)
                    return true;
                if (obj == null ||getClass() != obj.getClass())
                    return false;
                State other = (State) obj;
                if (wallRemoved != other.wallRemoved)
                    return false;
                if (x != other.x)
                    return false;
                if (y != other.y)
                    return false;
                return true;
            }

      }
      public static Queue<State> q = new LinkedList<>();
      public static HashSet<State> seen = new HashSet<>();

        public static State getPathBFS(int x, int y,int[][] arr) {

            q.add(new State(x,y,false, null));

            while(!q.isEmpty()) {
                State p = q.remove();

                if (arr[p.x][p.y] == 9) {
                    return p;
                }

                tryNext(p,p.x+1,p.y,arr);
                tryNext(p,p.x-1,p.y,arr);
                tryNext(p,p.x,p.y+1,arr);
                tryNext(p,p.x,p.y-1,arr);
            }
            return null;
        }

        public static void tryNext(State p, int x, int y, int[][]arr)  {
            if (x<0 || y<0 || x>=arr.length || y>=arr[x].length)
                return;
            State newState;
            if (arr[x][y] == 0 || arr[x][y]==9)  {
                newState = new State(x, y, p.wallRemoved, p);
            } else if (!p.wallRemoved) {
                newState = new State(x, y, true, p);
            } else {
                return;
            }
            if (seen.add(newState)) {
                q.add(newState);
            }
        }
    }

Upvotes: 2

zch
zch

Reputation: 15278

The thing to notice is the fact that shortest path through the maze with removing a section consists of two shortest paths - from entrance to removed section and then from removed section to exit.

You can use BFS to calculate distances of all positions in the maze from the starting location and distances of all positions from ending location.

Then you can find a position for which the sum of distances to entrance and to exit is minimal. If it is a wall section then it is the section to remove. Otherwise, removing any section is not useful.

Overall, this solution runs in linear time.

Upvotes: 3

Related Questions