Maze 迷宫算法的问题

Maze 迷宫算法的问题,maze,Maze,我遇到了一个设计用于解决迷宫的算法的问题 我在这里使用了一个算法 查找路径(x,y) 如果(x,y在迷宫外)返回false 如果(x,y是目标)返回true 如果(x,y未打开)返回false 将x、y标记为解决方案路径的一部分 if(FIND-PATH(x,y以北)=true)返回true if(FIND-PATH(x,y以东)=true)返回true if(FIND-PATH(x,y以南)=true)返回true if(FIND-PATH(x,y以西)=true)返回true 取消将x、y标

我遇到了一个设计用于解决迷宫的算法的问题

我在这里使用了一个算法

查找路径(x,y)

  • 如果(x,y在迷宫外)返回false
  • 如果(x,y是目标)返回true
  • 如果(x,y未打开)返回false
  • 将x、y标记为解决方案路径的一部分
  • if(FIND-PATH(x,y以北)=true)返回true
  • if(FIND-PATH(x,y以东)=true)返回true
  • if(FIND-PATH(x,y以南)=true)返回true
  • if(FIND-PATH(x,y以西)=true)返回true
  • 取消将x、y标记为解决方案路径的一部分
  • 返回错误
  • 这是一个递归解决方案,我对它进行了修改,使它即使在找到exit之后也能继续,以便它也能找到其他解决方案。它似乎奏效了,只是它似乎找到了我所知道的可能解决方案总数的一半

  • 如果(x,y是目标),则返回true将更改为返回false

  • 有人知道这样一个算法会导致可能的解决方案总数减少一半的问题吗?我在查找死端路径的总数时也遇到了问题,对此有什么建议吗?

    似乎缺少的是检查X&Y是否已被标记为解决方案的一部分,如果是,我们将中止。(这应该在第3.5点的某个地方)

    如果不是一个迷宫,某个地方可能有一个循环,那么它将不确定地运行并炸毁堆栈

    顺便说一下,从我读到的算法是基于一个只有一个解的迷宫


    R

    对于死胡同的数量,您需要这样的东西:


    3.5如果(x以北,y不开放)和(x以南,y不开放)和(x以西,y不开放)和(x以东,y不开放)死角+++

    而不是试图找到一条穿过迷宫的路,你需要找到(并因此绘制)穿过迷宫的多条路

    为了做到这一点,你需要标记你去过的地方(在一条特定的路径上)。如果你到达了一个你已经到达的点,你需要将该路线标记为死胡同

    递归函数仍然是一条路要走,但请确保通过递归函数传递(placesihaveBe)结构

    当到达N、S、E、W都被阻塞的点时,递归循环需要中断。(你以前去过那里,你不能往那个方向走,它在迷宫外面) 当到达目标时,递归循环也需要中断

    如果你达到了你的目标-增加一个全局变量。 如果你无处可去,增加一个死胡同

    我无法为此编写pcode(这将花费太长的时间),但我相信,如果N、S、E和W都被阻塞,则返回true的函数中存在秘密

    添加到我的初始答案中

    关于为什么我把我去过的地方视为“闭塞”,为什么我把它们视为死胡同

          ########
          #      #  
          # #### #
    ####### #  # #
            #  # #
    ####### #  # #
          # #### #
          #      #  
          ########
    
    我会将上面的迷宫部分归类为死胡同,如果不把我去过的地方视为阻塞区域,我看不出我如何能够识别出它

    我意识到这会导致下面的内容也显示出死胡同,但我找不到解决的办法

          #######
          #     #  
          # ### #
    ####### #G# #
            # # #
    ####### #   #
          # ### #
          #     #  
          #######
    

    我试着用java实现一个简单的算法。我的结论是,您描述的算法是有效的,即使是在寻找多条路径时也是有效的。或者,可能的话,你想出了一个比我更聪明的测试用例。(请张贴您的迷宫,以便我可以在其上尝试我的算法)

    我对死端计数器的实现可能不是最有效的,但它完成了任务。对于访问的每个当前打开的节点,它会检查周围的4个节点:

    • 如果至少有一个邻居是开放的,则当前节点不是死胡同
    • 如果访问了多个邻居,则当前节点不是死胡同
    • 如果只访问了一个节点(我们在上一步中访问的节点),而没有其他邻居打开,则当前节点是一个死胡同
    这是我写的java代码(注意!相当长)。另一种方法是,如果您愿意,将路径存储在堆栈上,每次将节点设置为已访问时推送节点,每次将节点设置为打开时弹出节点。每次达到目标时,应复制并保存保存当前路径的堆栈

    如果删除标有注释“死端调查步骤”的If块,则此实现几乎与问题中描述的实现完全相同

    package test;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class MazeSolver {
    
    final static int OPEN = 0;
    final static int WALL = 1;
    final static int GOAL = 2;
    final static int VISITED = 3;
    
    static int[][] field = { { 0, 0, 0, 0, 0, 1 }, { 1, 0, 1, 1, 0, 1 },
            { 1, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 1, 2 }, { 1, 0, 1, 0, 0, 0 } };
    
    // This is what the field looks like:
    //  
    // 0 1 1 0 1
    // 0 0 0 0 0
    // 0 1 1 0 1
    // 0 1 0 0 0
    // 0 0 0 1 0
    // 1 1 0 2 0
    
    static int width = field.length;
    static int height = field[0].length;
    static int xStart = 0;
    static int yStart = 0; // Initiated to start position: (x = 0, y = 0)
    static int nrSolutions = 0; // Records number of solutions
    
    // Used for storing id:s of dead end nodes.
    // The integer id is (x + y * width)
    static Set<Integer> deadEnds = new HashSet<Integer>();
    
    public static void main(String[] arg) {
        System.out.println("Initial maze:");
        printField();
    
        findPath(xStart, yStart);
    
        System.out.println("Number of solutions: " + nrSolutions);
        System.out.println("Number of dead ends: " + deadEnds.size());
    }
    
    private static void findPath(int x, int y) {
    
        if (x < 0 || y < 0 || x >= width || y >= height) { // Step 1
            return;
        } else if (field[x][y] == GOAL) { // Step 2
            nrSolutions++;
            System.out.println("Solution nr " + nrSolutions + ":");
            printField();
            return;
        } else if (field[x][y] != OPEN) { // Step 3
            return;
        } else if (isDeadEnd(x, y)) { // Extra dead-end-investigation-step
            int uniqueNodeId = x + y * width;
            deadEnds.add(uniqueNodeId); // Report as dead end
            return;
        }
    
        field[x][y] = VISITED; // Step 4
    
        findPath(x, y - 1); // Step 5, go north
        findPath(x + 1, y); // Step 6, go east
        findPath(x, y + 1); // Step 7, go south
        findPath(x - 1, y); // Step 8, go west
    
        field[x][y] = OPEN; // Step 9
    
        // Step 10 is not really needed, since the boolean is intended to
        // display only whether or not a solution was found. This implementation
        // uses an int to record the number of solutions instead.
        // The boolean return value would be (nrSolutions != 0)
    }
    
    private static boolean isDeadEnd(int x, int y) {
        int nrVisitedNeighbours = 0;
    
        if (y > 0) { // If northern neighbour exists
            if (field[x][y - 1] == VISITED) {
                nrVisitedNeighbours++;
            } else if (field[x][y - 1] != WALL) {
                return false;
            }
        }
        if (x < width - 1) { // If eastern neighbour exists
            if (field[x + 1][y] == VISITED) {
                nrVisitedNeighbours++;
            } else if (field[x + 1][y] != WALL) {
                return false;
            }
        }
        if (y < height - 1) { // If southern neighbour exists
            if (field[x][y + 1] == VISITED) {
                nrVisitedNeighbours++;
            } else if (field[x][y + 1] != WALL) {
                return false;
            }
        }
        if (x > 0) { // If western neighbour exists
            if (field[x - 1][y] == VISITED) {
                nrVisitedNeighbours++;
            } else if (field[x - 1][y] != WALL) {
                return false;
            }
        }
    
        if (nrVisitedNeighbours > 1) { // Circular path scenario
            return false;
        }
    
        return true; // The exhaustive result of this check: this is a dead
        // end
    }
    
    private static void printField() {
        for (int yy = 0; yy < field[0].length; yy++) {
            for (int xx = 0; xx < field.length; xx++) {
    
                System.out.print(field[xx][yy] + " ");
            }
            System.out.println();
        }
        System.out.println();
    }
    }
    
    此迷宫只有一个有效的解决方案路径。这是因为您只允许访问每个节点一次,因此绕过循环路径不是有效的解决方案路径,因为这将访问S以东和E以北的节点两次。解决方案路径的此定义由您使用的算法暗示

    如果一个人允许多次访问同一个节点,就会有无限多个解决方案,因为你可以绕着圆1、2、3。。。无限多次。

    正如您所说的,每次将节点设置为已访问时,我都会增加路径长度,每次将已访问的节点设置为打开时,我都会减少路径长度

    为了记录最短路径长度,我还有一个最短路径int值,我将其初始化为Integer.MAX_值。然后,每次我达到目标时,我都会这样做:

    if(pathLength < shortestPath){
        shortestPath = pathLength;
    }
    
    你还能找到更多吗? 还是我误解了你所说的死胡同? 我认为死胡同的意思是:你只能从一个方向到达一个节点

    例1:

    ######
    ## ###
    ## ### 
    ## ### 
    #S  E#
    ######
    
    上面的迷宫有一条死胡同

    例2:

    ######
    ##  ##
    ##  ## 
    ##  ## 
    #S  E#
    ######
    
    上面的迷宫没有死胡同。即使您位于最北边的一个可访问节点上,仍然有两个相邻的非墙正方形

    你对死胡同还有别的定义吗?

    这是一个迷宫示例

    ####################
    #S #  #       #    #
    #  # ##  ##  ### ###
    #     #   #   #    #
    ## #  #   #     ## #
    #     ### #####    #
    # #   #   #  #   ###
    # ### ### ## # #####
    #  #      #       E#
    ####################
    

    我想是3。你能试着制作一个有多个这样的解决方案的小迷宫,并把它作为ASCII图发布在这里,这样我们就可以试着看看为什么它找不到它们了
    ######
    ##  ##
    ##  ## 
    ##  ## 
    #S  E#
    ######
    
    ####################
    #S #  #       #    #
    #  # ##  ##  ### ###
    #     #   #   #    #
    ## #  #   #     ## #
    #     ### #####    #
    # #   #   #  #   ###
    # ### ### ## # #####
    #  #      #       E#
    ####################