迷宫路径求解

一.问题描述

现有一个迷宫,以S作为起点,G作为终点,#作为墙壁,. 作为可以通行的道路,求从起点到终点的路径。多条路径,分别输出。

例如,输入:( 为便于观看,下图做了处理,输入 4  4 表示迷宫的长度(X轴)和宽度(Y轴)

输出从S到G点的行走路径,要求不能绕圈,即走过的点不能再走。

结果如下:(表示有两条路线)

(1,2),(2,2),(3,2),(3,1),(3,0),
(1,2),(1,3),(2,3),(2,2),(3,2),(3,1),(3,0),

二. 问题解析

如果是人进行观察图像,很容易就能知道上图中的迷宫,可以一直向下走,然后左拐,OK!坐标(1,1)点位置,虽然也是可达的一个点,但从图像上很容易就可以看出,后面的路都是死的,也就是不可能通过这个点到达终点,但是计算机在运算时实际上,不可能知道这一点,它必须要经过一个尝试才知道这条路不通。这一点,大多数人其实也会经历这个过程,甚至于有的人会反复的犯同样的错误。

简而言之,计算机绘制路径,需要遍历所有可以走的路径,其中的一条路径或几条可以到达终点,就是我们需要的路径。

方式有很多种,下面我介绍一种利用递归遍历所有情况求解的方式,在图论中称作深度优先遍历。

基本思路就是,从初始状态,不断的转移最近的状态,如果不能再转移,就回退到上一步,继续进行状态转移。直到状态到达需要的状态就返回,甚至可以遍历到所有的状态。

对于上图中,可以理解成,从S(开始点)不断遍历当前状态可达的最近状态,再由它能遍历的每一个下一个状态作为初始状态,再遍历可达的状态,也就是每一个状态只考虑自己当前可以达到什么状态,当然从整体上也需要有所约束,就像递归算法的思路,不断的将问题缩小成一个小问题,但是需要一个条件作为递归的最底层,从而跳出递归。

在迷宫问题中,显然一条不走重复路线经过点最多也就是是迷宫所拥有的点,或者某条线路已经到达了终点,就可以结束了,因此代码条件判断可以写为

// 如果已经到达终点,则遍历路线,并返回
if (p.x==E.x&& p.y==E.y){
     for ( int  i= 0 ;i<index;++i){
         System.out.print( "(" +fullPath[i].x+ "," +fullPath[i].y+ ")," );
     }
     System.out.println();
     return ;
}
// 如果探索的路线已经达到可能的最大值,则返回
if (index>=fullPath.length){
     return ;
}

在迷宫中,我们可以想到的是,一个点如果已经经过,那么通过其他方式再回到这个点的时候是没有意义的,可以认为选择的路线是一个回路,也就是一条错误的道路。就需要剪枝操作,因此,我们需要一个变量记录,经过点的状态。

我们用一个布尔值记录每个点的状态,并且将每次遍历的时候经过的点,标记为已经经过。如下代码中valid表示是否经过,注意的是,在一次循环递归中,到结束的时候,已经表示这条道的所有可能的情况都已经遍历结束,需要将是否经过的标识为再次设置为未经过,为之后的遍历中提供可以经过的标识。

// 移动在迷宫的范围内,并且不是墙,还没有在之前的线路中经过
if (nx>= 0 &&nx<N&&ny>= 0 &&ny<M&&map[nx][ny]!= '#' &&valid[p.x][p.y]== false ){
     // 设置为已经过
     valid[p.x][p.y]= true ;
     fullPath[index]= new  Point(nx,ny);
     dfs(fullPath[index],index+ 1 );
     // 这种状态下的,所有可能状态都遍历完毕,回退到上一种状态中
     valid[p.x][p.y]= false ;
}

在dfs(Depth-First Search)算法中,需要将初始状态传递给参数。

例如:

// S 是起始点,0代表步数
dfs(S, 0 );

三.完整示例代码

3.1 JAVA源码

import  java.util.Scanner;
public  class  CopyOfMaze {
     
     public  static  Scanner scanner= new  Scanner(System.in);
     class  Point{
         int  x;
         int  y;
         Point( int  x, int  y){
             this .x=x;
             this .y=y;
         }
     }
     
     /**
      * 方向分别为 东,南,西,北四个方向
      */
     int [][] direction={{ 1 , 0 },{ 0 , 1 },{- 1 , 0 },{ 0 ,- 1 }};
     
     char [][] map;
         
     Point S,E;
         
     public  void  dataInput(){
         // 初始化地图
         map= new  char [N][M];
         for ( int  i= 0 ;i<N;++i){
             String buff = scanner.nextLine();
             for ( int  j= 0 ;j<M;++j){
                 map[i][j]=buff.charAt(j);
                 if (map[i][j]== 'S' ){
                     S= new  Point(i,j);
                 } else  if (map[i][j]== 'G' ){
                     E= new  Point(i,j);
                 }
             }
         }
     }
     
     int  N,M;
     
     Point[] fullPath;
     boolean [][] valid;
     
     public  void  init(){
         fullPath= new  Point[N*M];
         valid= new  boolean [N][M];
     }
     public  void  dfs(Point p, int  index){
         // 如果已经到达终点,则遍历路线,并返回
         if (p.x==E.x&& p.y==E.y){
             for ( int  i= 0 ;i<index;++i){
                 System.out.print( "(" +fullPath[i].x+ "," +fullPath[i].y+ ")," );
             }
             System.out.println();
             return ;
         }
         // 如果探索的路线已经达到可能的最大值,则返回
         if (index>=fullPath.length){
             return ;
         }
         for ( int  i= 0 ;i<direction.length;++i){
             // 移动向量
             int  nx=p.x+direction[i][ 0 ];
             int  ny=p.y+direction[i][ 1 ];
             // 移动在迷宫的范围内,并且不是墙,还没有在之前的线路中经过
             if (nx>= 0 &&nx<N&&ny>= 0 &&ny<M&&map[nx][ny]!= '#' &&valid[p.x][p.y]== false ){
                 // 设置为已经过
                 valid[p.x][p.y]= true ;
                 fullPath[index]= new  Point(nx,ny);
                 dfs(fullPath[index],index+ 1 );
                 // 这种状态下的,所有可能状态都遍历完毕,回退到上一种状态中
                 valid[p.x][p.y]= false ;
             }
         }
     }
     
     public  void  solve(){
         dataInput();
         init();
         dfs(S, 0 );
     }
     
     public  static  void  main(String[] args){
         CopyOfMaze maze= new  CopyOfMaze();
         maze.N=scanner.nextInt();
         maze.M=scanner.nextInt();
         scanner.nextLine();
         maze.solve();
     }
}

3.2 测试用例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值