zoukankan      html  css  js  c++  java
  • 老鼠走迷宫问题

    问题:题目:在二维数组中使用2来代表墙壁,1来代表老鼠的行径,试写出代码求得老鼠由入口走到出口的路线(一条便可)。输入:输入一个二维数,2代表墙壁,0代表空表示可走),再输入起点和终点坐标,输出整个数组,2代表墙壁,将可行路线(一条)中的0换成1。

    还可以换一种 描述:

    有一个迷宫,在迷宫的某个出口放着一块奶酪。将一只老鼠由某个入口处放进去,它必须穿过迷宫,找到奶酪。请找出它的行走路径。

    解法:这个问题可以用递归的方法去求解。

      绿色箭头指向的是迷宫的入口,将小老鼠由此放入,红色箭头的位置就是奶酪的所在地,小老鼠必须穿过迷宫从红色箭头标示的地方走出去。

    我们用一个二维数组来表示迷宫,用2表示迷宫的墙壁,即上图中的黑色部分,用0表示通路,即上图中老鼠可以行走空白的格子。老鼠每走到一个格子的时候就将该位置的值置为1,表示老鼠的行走路径包括这个格子。

            接下来说一下老鼠的行走策略:老鼠每走一步(格子)的时候,将该格子的值置为1,然后会依次考察该格子的右、下、左、上位置的格子是否可走(可走的标志是该格子的值为0)。这样老鼠从入口的格子出发,就会派生出很多可能的行走路径,如果某一条路不能走到终点的话,那么就要将相应的格子的值重新置为0了,表示正确的路径不包括这个格子。

    伪代码:

    Procedure GO(maze[])
        VISIT(maze, START_I, START_J, END_I, END_J)
        
    Procedure VISIT(maze[], i, j, end_i, end_j)
        IF maze[i][j] == 0
            maze[i][j] = 1
            IF maze[end_i][end_j] == 0
               IF !(VISIT(maze, i, j + 1, end_i, end_j) OR 
                    VISIT(maze, i + 1, j, end_i, end_j) OR
                    VISIT(maze, i, j - 1, end_i, end_j) OR 
                    VISIT(maze, i - 1, j, end_i, end_j))
                   maze[i][j] = 0
        RETURN maze[end_i][end_j] == 1

    c++:

    #include<iostream>
    using namespace std;
    
    #define N 7int visit(int maze[N][N],int i,int j,int endI,int endJ)
    {
        if(maze[i][j]==0)
        {
            maze[i][j]=1;
            if(maze[endI][endJ]==0)
            {
                if(! ( visit(maze,i,j+1,endI,endJ) ||
                       visit(maze,i+1,j,endI,endJ) ||
                       visit(maze,i,j-1,endI,endJ) ||
                       visit(maze,i-1,j,endI,endJ) ) )
                {
                    maze[i][j]=0;
                }
            }
        }
        return maze[endI][endJ];
    }
    void print(int maze[][N])
    {
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                switch(maze[i][j])
                {
                    case 0: cout<<"0"; break;
                    case 1:cout<<"1"; break;
                    case 2:cout<<"2";break;
                }
            }
            cout<<endl;
        }
    }
     
    
    int main()
    {
        int maze[N][N]={{2, 2, 2, 2, 2, 2, 2}, 
                                {2, 0, 0, 0, 0, 0, 2}, 
                                {2, 0, 2, 0, 2, 0, 2}, 
                                {2, 0, 0, 2, 0, 2, 2}, 
                                {2, 2, 0, 2, 0, 2, 2}, 
                                {2, 0, 0, 0, 0, 0, 2}, 
                                {2, 2, 2, 2, 2, 2, 2}};
        if(!visit(maze, 1,1,5, 5 )) {
            printf("
    沒有找到出口!
    "); 
        }
        print(maze);
        
    }

    下面的代码更好理解:

    分析:详解分析:由题目可知从起点开始逐个判断相邻格子的可行性是最显而易见的方法。若下一个格子可行(值为0 0),则进入此格判断此格的相邻格,反之换下一个方位的格子再进行判断,直到格子的坐标与终点一致则说明路线可行,将沿途所有的‘0’变成‘1’。这时你可能郁闷,该怎样将所有的可行‘0’变为‘1’?其实,在老鼠每移动一格时,若碰到‘0’即可将之变成‘1’,当路线不合理(所有方位都不存在‘0’)时返回再将其变回‘0’,剩下的‘1’就全是可行格了。当然,这样必须得用到递归了。具体思想是:编写可递归函数judge,当格子可行则递归继续执行judge函数,格子不可行(判断可行与否可用一个变量success是否等于1实现)则返回,返回时将‘1’变成‘0’。judge函数代码显而易见了.

      

    int success=0;    //初始值为0
    int a[m][n];      //迷宫数组
    int judge(int i,int j,int x,int y)            //judge函数,i,j为每次判断格子坐标;x,y是终点坐标
    {
     a[i][j]=1;                            //每个当前可行格标记为‘1’
     if(i==x && j==y) success=1;           //到达终点时标记success为1
     if(success!=1 && a[i][j+1]==0) judge(i,j+1,x,y);      //以下是四个方位格子可行性的判断
     if(success!=1 && a[i+1][j]==0) judge(i+1,j,x,y);
     if(success!=1 && a[i-1][j]==0) judge(i-1,j,x,y);
      if(success!=1 && a[i][j-1]==0) judge(i,j-1,x,y);
    if(success!=1)              //每次返回时若此路不通将‘1’还原为‘0’
       a[i][j]=0;
    return success;             //返回值为success
    }

    非递归:

    /* dfs(深度优先算法)算法 走迷宫 */
    /* int maze[5][5] = { */
    /*  0, 1, 0, 0, 0, */
    /*  0, 1, 0, 1, 0, */
    /*  0, 0, 0, 0, 0, */
    /*  0, 1, 1, 1, 0, */
    /*  0, 0, 0, 1, 0, */
    /* }; */
    /* 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜
       着走,要求编程序找出从左上角到右下角的路线。程序如下: */
    
    
    /* 这次堆栈里的元素是结构体类型的,用来表示迷宫中一个点的x和y坐标。我们用一个新
       的数据结构保存走迷宫的路线,每个走过的点都有一个前趋(Predecessor)点,表示
       是从哪儿走到当前点的,比如predecessor[4][4]是坐标为(3, 4)的点,就表示从(3,
       4)走到了(4, 4),一开始predecessor的各元素初始化为无效坐标(-1, -1)。在迷宫中
       探索路线的同时就把路线保存在predecessor数组中,已经走过的点在maze数组中记为
       2防止重复走,最后找到终点时就根据predecessor数组保存的路线从终点打印到起点
        */
    
    /* 每探索一步都打印出当前迷宫的状态(标记了哪些点),从打印结果可以看出这种搜索
       算法的特点是:每次探索完各个方向相邻的点之后,取其中一个相邻的点走下去,一直
       走到无路可走了再退回来,取另一个相邻的点再走下去。这称为深度优先搜索(DFS,
       Depth First Search)。探 */
    /* 如果在探索问题的解时走进了死胡同,则需要退回来从另一条路继续探索,这种思想称
       为回溯(Backtrack),一个典型的例子是很多编程书上都会讲的八皇后问题。 */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <malloc.h>
    #include <string.h>
    #define MAX_ROW 5
    #define MAX_COL 5
    
    struct point {
      int x, y;
    } stack[215];
    
    int top=0;
    
    void push(struct point p){
      stack[top++]=p;
    }
    
    struct point pop(){
      return stack[--top];
    }
    
    int is_empty(){
      return top==0;
    }
    
    int maze[MAX_ROW][MAX_COL] = {
      0, 1, 0, 0, 0,
      0, 1, 0, 1, 0,
      0, 0, 0, 0, 0,
      0, 1, 1, 1, 0,
      0, 0, 0, 1, 0,
    };
    /* 记录每个节点的前驱是哪个节点,即从哪个节点来到此节点的 */
    struct point predecessor[MAX_ROW][MAX_COL]={
      {  {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}  },
      {  {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, },
      {  {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, },
      {  {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, },
      {  {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, }
    };
    
    void visit(int row,int col,struct point pre){
      struct point cur ={row,col};
      maze[row][col]=2;             /* 2 表示这个节点被访问过 */
      if(!(row==0&&col==0)){        /* 0,0没有前驱节点,无从加起 */
        predecessor[row][col]=pre;
      }
      push(cur);
    }
    
    void print_maze(){
      int i,j;
      for ( i = 0; i < MAX_ROW; ++i) {
        for ( j = 0; j< MAX_COL; ++j) {
          printf ("%d, ",maze[i][j]);
        }
        printf ("
    ");
      }
      printf ("
    ");
    }
    int main(int argc, char *argv[]){
      struct point p={0,0};
      visit(0,0,p);
      /* push(p); */
    
      while(!is_empty()){
        p=pop();
        printf ("current=%d,%d
    ",p.x,p.y);
        if (p.x==MAX_ROW-1&&p.y==MAX_COL-1){
          break;
        }
        if(p.y<MAX_COL-1 && maze[p.x][p.y+1]==0){        /* right */
          visit(p.x,p.y+1,p);
        }
        if(p.x<MAX_ROW-1 && maze[p.x+1][p.y]==0){        /* down */
          visit(p.x+1,p.y,p);
        }
        if(p.y>0 && maze[p.x][p.y-1]==0){        /* left */
          visit(p.x,p.y-1,p);
        }
        if(p.x>0 && maze[p.x-1][p.y]==0){        /* up */
          visit(p.x-1,p.y,p);
        }
        print_maze();
      }
      if (p.x==MAX_ROW-1 && p.y==MAX_COL-1){ /* 如果到达了终点,打印回路 */
        printf ("%d,%d
    ",p.x,p.y);
        while(predecessor[p.x][p.y].x!=-1){
          p=predecessor[p.x][p.y];
          printf ("%d,%d
    ",p.x,p.y);
        }
      }
      return 0;
    }

    转自:http://jixiuf.github.io/c/dfs_maze.html

    由於迷宮的設計,老鼠走迷宮的入口至出口路徑可能不只一條,如何求出所有的路徑呢?

      解:如果迷宫的设计使得走法不止一种,则只要在老鼠走至出口时显示出所有的路径,然后退回上一格重新选择下一个位置继续递归就可以了。

    伪代码:

    Procedure GO(maze[])
        VISIT(maze, START_I, START_J, END_I, END_J)
        
    Procedure VISIT(maze[], i, j, end_i, end_j)
        IF maze[i][j] == 0
            maze[i][j] = 1
            IF maze[end_i][end_j] == 1
                PRINT maze
            ELSE
                VISIT(maze, i, j + 1, end_i, end_j)
                VISIT(maze, i + 1, j, end_i, end_j)
                VISIT(maze, i, j - 1, end_i, end_j)
                VISIT(maze, i - 1, j, end_i, end_j)
            maze[i][j] = 0

    c++:

    #include<iostream>
    using namespace std;
    
    #define N 9
     
    
    void print(int maze[][N]);
     
    //打印所有路径
    void visit(int maze[N][N],int i,int j,int endI,int endJ)
    {
        if(maze[i][j]==0)
        {
            maze[i][j]=1;
    
            if(maze[endI][endJ]==1)
                print(maze);
    
            else
            {
                 visit(maze,i,j+1,endI,endJ);
                 visit(maze,i+1,j,endI,endJ);
                 visit(maze,i,j-1,endI,endJ);
                 visit(maze,i-1,j,endI,endJ);
                 
            }
    
            maze[i][j]=0;
        }
         
    }
    void print(int maze[][N])
    {
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                switch(maze[i][j])
                {
                    case 0: cout<<"0"; break;
                    case 1:cout<<"1"; break;
                    case 2:cout<<"2";break;
                }
            }
            cout<<endl;
        }
        cout<<"--------"<<endl;
    }
     
    
    int main()
    {
     
         int maze[N][N] = {{2, 2, 2, 2, 2, 2, 2, 2, 2},
                                {2, 0, 0, 0, 0, 0, 0, 0, 2},
                                {2, 0, 2, 2, 0, 2, 2, 0, 2},
                                {2, 0, 2, 0, 0, 2, 0, 0, 2},
                                {2, 0, 2, 0, 2, 0, 2, 0, 2},
                                {2, 0, 0, 0, 0, 0, 2, 0, 2},
                                {2, 2, 0, 2, 2, 0, 2, 2, 2},
                                {2, 0, 0, 0, 0, 0, 0, 0, 2},
                                {2, 2, 2, 2, 2, 2, 2, 2, 2}}; 
    
        visit(maze, 1,1,5, 5);
        
        
    }

    参考:http://openhome.cc/Gossip/AlgorithmGossip/MouseGoMaze2.htm

    http://blog.csdn.net/zhutulang/article/details/7539692

    问题变种:

    http://blog.sina.com.cn/s/blog_9683899a0101fzpn.html

    http://www.csie.ntnu.edu.tw/~u91029/StateSpaceSearch.html

  • 相关阅读:
    !!!!Linux系统开发 系列 4 进程资源 环境 fork()子进程 wait() waitpid()僵尸 孤儿进程
    linux运维工程师
    C# CSGL
    C# 中的"yield"使用
    C#语法糖
    VS2017下Git的使用
    Oracle数据类型与.NET中的对应关系
    Java 8 Stream
    Java 8 默认方法
    Java 8 函数式接口
  • 原文地址:https://www.cnblogs.com/youxin/p/3354661.html
Copyright © 2011-2022 走看看