迷宫问题,搜索,广度优先搜索,Java

本文介绍了如何使用宽度优先搜索(BFS)算法解决迷宫问题,从起点到终点的最短路径查找,包括广度优先搜索的原理、代码实现和处理多路径的策略。通过实例演示了如何在Java中利用队列数据结构实现迷宫探索,适用于寻找最短路径的应用场景。
摘要由CSDN通过智能技术生成
题目 1672: 迷宫问题

时间限制: 1Sec 内存限制: 32MB 提交: 1760 解决: 448

题目描述
小明置身于一个迷宫,请你帮小明找出从起点到终点的最短路程。
小明只能向上下左右四个方向移动。
输入
输入包含多组测试数据。输入的第一行是一个整数T,表示有T组测试数据。
每组输入的第一行是两个整数N和M(1<=N,M<=100)。
接下来N行,每行输入M个字符,每个字符表示迷宫中的一个小方格。
字符的含义如下:
‘S’:起点
‘E’:终点
‘-’:空地,可以通过
‘#’:障碍,无法通过
输入数据保证有且仅有一个起点和终点。
输出
对于每组输入,输出从起点到终点的最短路程,如果不存在从起点到终点的路,则输出-1。

解题思路:
本题使用深度优先搜索会超时,所以使用广度优先搜索,也叫作宽度优先搜索。

深搜与广搜相似,都是“穷竭搜索”。但他们也有不同之处,不同之处在于搜索的顺序。

广搜总是先搜索距离初始状态最近的状态。也就是说,

它时按照开始状态 ->只需1次就可以到达的所有状态
->只需2次就可以到达的所有状态……这样的顺序进行搜索。

对于同一个状态,广度优先搜索只经过一次,

因此复杂度为O(状态数 X 转移的方式)。

深搜(隐式地)利用了栈(后进先出)进行计算,而广搜则利用了队列(先进后出)。

搜索时首先将初始状态添加到队列里,此后从队列的最前端不断取出状态,把从该状态可以转移到的状态中尚未访问过的部分加入队列,

如此往复,直到队里被取空了找到了问题的解。

宽度优先搜索按照距离初始状态由近及远的顺序进行搜索,因此比较适合求最短路径、最少操作之类的问题。

在这个问题中,我们构造一个Q类 用来存储x,y,dept,分别是坐标(x,y) 第dept步。

因为要向四个方向移动,用 int next[][]={{0,1},{1,0},{0,-1},{-1,0}};来表示四个方向,这样可以通过循环来实现四个方向的移动遍历。

在向四个方向遍历移动前,去除队列最前端的元素,判断是否是终点。如果是,则程序结束,得到结果。

在向四个方向移动中,还是和dfs一样的判断,判断是否越界,是否可以通过,是否未走过,这里需要注意的是广搜不需要取消走过的标记,

因为广搜是一层层的搜索,而不用回溯。

在向四个方向移动中,如果可以移动,那么将下一个点加入到队列的结尾,并且步数加一。


参考代码

import java.awt.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main{
    public static class Q{
        int x;
        int y;
        int dept;//当前步数
    }
    public static void main(String[] args) {
        //队列,queue,先进先出
            //在java中,LinkedList实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
            //所以本题用LinkedList代替Queue。
        //bfs.广度优先搜索,层层递进
        //使用队列存储起点,        
        //遍历四个方向,如果新方向可以走
            //则将新方向存储到队列
        //当四个方向遍历完成后,将首个出列 poll(),并得到首列的元素。重复上一步,即向四个方向遍历。
        //假如遍历到某个点时,刚好是终点,那么结束循环,这就是结果。
        Scanner sc=new Scanner(System.in);
        int next[][]={{0,1},{1,0},{0,-1},{-1,0}};//下一步
        int t=sc.nextInt();
        while (t-->0) {//组数
            int n=sc.nextInt();
            int m=sc.nextInt();
            int sx=0,sy=0;//起点坐标
            int gx=0,gy=0;//终点坐标
            LinkedList<Q> que=new LinkedList<Main.Q>();
            char arr[][]=new char[n][m];
            int flag[][]=new int[n][m];//标记是否走过
             
            //输入
            for (int i = 0; i < n; i++) {
                String str=sc.next();
                for (int j = 0; j <str.length(); j++) {
                    arr[i][j]=str.charAt(j);
                    if (arr[i][j]=='S') {//起点
                        sx=i;sy=j;
                    }
                    if (arr[i][j]=='E') {//终点
                        gx=i;gy=j;
                    }
                }
            }
            //开始广搜
            flag[sx][sy]=1;//标记开始坐标
            Q q=new Q();//初始化开始坐标
            q.x=sx;
            q.y=sy;
            q.dept=0;
            que.add(q);//添加
            int finish=0,result=0;
            while (que.size()!=0) {
                Q first=que.poll();//取出头列,并删除
                if (first.x==gx && first.y==gy) {
                    finish=1;
                    result=first.dept;
                    break;
                }
                for (int i = 0; i <4; i++) {//右下左上 4 个步骤
                    //如果已经到达终点,那么退出循环,并且得到结果
                    int tx=first.x+next[i][0];
                    int ty=first.y+next[i][1];
                    if (tx>=0 && tx<n && ty>=0 && ty<m 
                        && arr[tx][ty]!='#' && flag[tx][ty]==0) {//符合条件
                        flag[tx][ty]=1;//标记走过
                        Q temp=new Q();
                        temp.x=tx;
                        temp.y=ty;
                        temp.dept=first.dept+1;
                        que.add(temp);//添加一个坐标到队列
                    }
                }
                if (finish==1) {//如果已经得到结果,那么直接退出
                    break;
                }
            }
            //输出结果
            if (result==0) {
                System.out.println("-1");
            }else {
                System.out.println(result);
            }
        }
        
         
}
   
}

题目 1923: [蓝桥杯][算法提高VIP]学霸的迷宫

时间限制: 1Sec 内存限制: 128MB 提交: 1040 解决: 232

题目描述
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入
第一行两个整数n, m,为迷宫的长宽。
接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出
第一行一个数为需要的最少步数K。
第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
3 3
001
100
110
样例输出
4
RDRD

思路与迷宫问题相同,迷宫问题题解 ,宽度搜索。

有两点不同:

1,getDir()方法,根据向四个方向的移动,得到对应的字符。

2,int next[][]={{1,0},{0,-1},{0,1},{-1,0}};//下一步 ,D L R U

根据题目要求 如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。所以next的顺序也应当做些改变。

完整代码:

import java.awt.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main{
    public static class Q{
        int x;
        int y;
        int dept;//当前步数
        String track;//路线
    }
     
  //根据i的值,返回RDLU;
     public static String getDir(int i){
        if (i==0) {
            return "D";
        }
        if (i==1) {
            return "L";
        }
        if (i==2) {
            return "R";
        }
        if (i==3) {
            return "U";
        }
        return "error";
     }
     
    public static void main(String[] args) {
        //队列,queue,先进先出
            //在java中,LinkedList实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
            //所以本题用LinkedList代替Queue。
        //bfs.广度优先搜索,层层递进
        //使用队列存储起点,        
        //遍历四个方向,如果新方向可以走
            //则将新方向存储到队列
        //当四个方向遍历完成后,将首个出列 poll(),并得到首列的元素。重复上一步,即向四个方向遍历。
        //假如遍历到某个点时,刚好是终点,那么结束循环,这就是结果。
        Scanner sc=new Scanner(System.in);
        int next[][]={{1,0},{0,-1},{0,1},{-1,0}};//下一步 ,D L R U
            int n=sc.nextInt();
            int m=sc.nextInt();
            int sx=0,sy=0;//起点坐标
            int gx=n-1,gy=m-1;//终点坐标
            LinkedList<Q> que=new LinkedList<Main.Q>();
            char arr[][]=new char[n][m];
            int flag[][]=new int[n][m];//标记是否走过
              
            //输入
            for (int i = 0; i < n; i++) {
                String str=sc.next();
                for (int j = 0; j <str.length(); j++) {
                    arr[i][j]=str.charAt(j);
                }
            }
            //开始广搜
            flag[sx][sy]=1;//标记开始坐标
            Q q=new Q();//初始化开始坐标
            q.x=sx;
            q.y=sy;
            q.dept=0;
            q.track="";
            que.add(q);//添加
            int result=0;//结果步数
            String resultSract="";//结果路线
            while (que.size()!=0) {
                Q first=que.poll();//取出头列,并删除
                if (first.x==gx && first.y==gy) {
                    result=first.dept;//改变步数
//                  if (result==first.dept) {//如果步数相等
//                      if (resultSract.compareTo(first.track)>0) {//按结果的字典比较
                             resultSract=first.track;
//                      }
//                  }
                }
                 
                for (int i = 0; i <4; i++) {//右下左上 4 个步骤
                    //如果已经到达终点,那么退出循环,并且得到结果
                    int tx=first.x+next[i][0];
                    int ty=first.y+next[i][1];
                    if (tx>=0 && tx<n && ty>=0 && ty<m 
                        && arr[tx][ty]!='1' && flag[tx][ty]==0) {//符合条件
                        flag[tx][ty]=1;//标记走过
                        Q temp=new Q();
                        temp.x=tx;
                        temp.y=ty;
                        temp.dept=first.dept+1;
                        temp.track=first.track+getDir(i);
                        que.add(temp);//添加一个坐标到队列
                    }
                }
            }
            //输出结果
            if (result==0) {
                System.out.println("-1");
            }else {
                System.out.println(result);
                System.out.println(resultSract);
            }
         
         
          
}
    
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值