【问题标题】:Binary Maze with a Trick有技巧的二元迷宫
【发布时间】:2017-04-29 22:12:18
【问题描述】:

问题是一个二元迷宫,其中 1 是墙壁,0 是有效路径。你从左上角(0,0)开始,你必须到达右下角(宽度-1,高度-1)。您必须找到从左上角到右下角的最短路径。出现扭曲是因为您最多只能拆除一堵墙,这是让我感到困惑的部分。我当前的代码可以解决最短路径,而无需计算要拆除的墙。

这里有几个例子: [0,1,1,0], [0,0,0,1], [1,1,0,0], [1,1,1,0] 答:7招(包括进出)

示例 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] 答案:11(因为你可以在[0][1]位置打破墙,让路径变短)

所以就像我之前所说的,我的代码会找到最短路径,但目前不会尝试为更短的路径移除墙,主要是因为我不明白如何去做。我在想我一次移除一堵墙并不断重新运行以查看是否产生了更短的路径,但这似乎非常昂贵的操作,但它可能会完成工作。不过,我希望我能找到一种更清晰、更简单的方法。

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;
        }
    }

【问题讨论】:

    标签: java breadth-first-search maze graph-layering


    【解决方案1】:

    需要注意的是,移除一个部分后穿过迷宫的最短路径由两条最短路径组成——从入口到移除部分,然后从移除部分到出口。

    您可以使用 BFS 计算迷宫中所有位置到起始位置的距离以及所有位置到结束位置的距离。

    然后,您可以找到入口和出口距离之和最小的位置。如果它是墙壁部分,那么它就是要移除的部分。否则,删除任何部分都没有用。

    总的来说,这个解决方案在线性时间内运行。

    【讨论】:

    • 我不太明白你的思路。从开始到结束和结束到开始的距离不是一样的吗?如果它是一堵墙,我也不能真正在它上面运行算法。
    • @GreenSkies,通过迷宫的距离 = 距离(入口,拆除墙)+ 距离(出口,拆除墙)。该算法应该忽略穿过墙壁的路径,但最终容忍墙壁。
    • 很好的解决方案。似乎两端 BFS 到达同一个目的地。
    【解决方案2】:

    您使用 Point 类来表示玩家在穿过迷宫时的位置。如果没有墙规则,您只需了解玩家的这个位置以确定他下一步可以做什么,因此您可以在所有可能位置的有向图上进行 BFS 以找到路径。

    通过墙规则,为了确定玩家接下来可以去哪里,你还必须知道他是否已经移除了墙,所以玩家的完整状态不仅包括他的位置,还包括一个布尔值,指示墙是否已被移除。

    然后,您可以在这些扩展状态图上执行 BFS,以找到最多移除一堵墙的最短路径。

    由于您已经发布了针对更简单问题的实际代码,我将使用展开状态(和一个 Set 以防止您访问同一状态两次,而不是修改 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);
                }
            }
        }
    

    【讨论】:

    • 该解决方案非常有效,但我并不完全了解它是如何工作的。它是拆除所有可能的墙,还是对拆除的墙更具战略性。
    • 考虑状态图并查看 tryNext()。如果 (x+1,y) 里面有一堵墙,那么只有在您还没有拆除一堵墙的情况下,您才能从 (x,y) 到达那里,并且去那里需要拆除那块墙。所以状态(我在 (x,y) 并且没有移除一堵墙)连接到状态(我在 (x+1,y) 并且已经移除了一堵墙)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    相关资源
    最近更新 更多