(BFS)迷宫寻路算法

在这里插入图片描述行远必自迩 登高必自卑

来自蓝桥杯oj
在这里插入图片描述在这里插入图片描述
解法
这一题属于典型的迷宫搜索类题,刚入手时我也是一脸茫然,但是仔细思考了一晚上,还是勉强的解决了,这道题属于经典的BFS搜索题,解决的核心在于,如何找到迷宫出口,还有如何存储路径
解法1:BFS+路径存储
作为图论中经典的搜索算法之一,BFS算法搜索路径的特点是"层层搜索",映射到这个题目上,给我的理解就是,使用BFS这个特点,一旦我们搜索到终点,好吧,整个过程也可以结束了,因为BFS每一步的遍历都是基于全局考虑的,所以一旦搜索到终点,不用想了,搜索的次数一定是最短的,基于这个特点,对于第一行要求输出最短路径数,我们就可以根据这个特点来计算,对于第二行要我们输出路径,这也很好办,我们只需要在结点类里再封装两个属性(当前所走的轨迹,上一个结点的信息)

//结点类
class N{
	int x;//当前结点的横坐标
	int y;//当前结点的纵坐标
	N pre;//前趋结点,用来回溯路径
	String dir;//用于输出方向信息
	N(int x,int y,String d,N pr){
		this.x = x;
		this.y = y;
		this.dir = d;
		this.pre = pr;
	}
}

下面我们来实现这个算法,BFS遍历需要借助的数据结构是队列,所以我们使用Java里已经封装好的Queue,上一段刚刚已经AC的code

/**
	 * 
	 * @param map 地图
	 * @param n 地图长度
	 * @param m 地图宽度
	 */
	static void BFS(int[][] map,int n,int m) {
		/*
		 * 使用广度优先策略
		 */
		
		/*
		 * 方向数组
		 */
		int[][] dis = {
				{1,0},//下
				{0,-1},//左
				{0,1},//右
				{-1,0},//上
		};
		/*
		 * 轨迹数组,注意,一定要与dis数组相对应
		 * 思考,为什么一定要按这个顺序?因为题目给出路径输出要按字典序输出,所以我们要优先将字典序小的"方向字母"先入队(先访问)
		 */
		String[] str = {"D","L","R","U"};
		/*
		 * 标记数组,用于判断当前结点是否已经访问
		 */
		int[][] book = new int[n+1][m+1];
		/*
		 * 队列
		 */
		Queue<N> Q = new LinkedList<N>();
		Q.add(new N(1,1,"",null));//起点入队
		book[1][1] = 1;//标记已经走过了
		while(!Q.isEmpty()) {//如果队列不为空,还可继续遍历
			N p = Q.peek();//寻找下一步可以走的结点
			for(int i=0;i<4;i++) {
				//遍历四个方向
				int x = p.x+dis[i][0];
				int y = p.y+dis[i][1];
				//边界判断
				if(x<1 || x>n || y<1 || y>m || map[x][y]==1 || book[x][y]==1) {
					continue;
				}
				if(map[x][y]==0 && book[x][y]==0) {
					//找到可以走的结点,入队
					Q.add(new N(x,y,str[i],p));
					if(x==n && y==m) {
						//如果此时已经到达终点,开始输出路径
						N temp = Q.peek();
						int step = 0;//记录最小步数
						StringBuffer ans = new StringBuffer();//由于是逆序(从终点到起点)记载路径,所以我们最终还要使用append反转一下
						if(temp.x+1 == n) {//因为会出现最后一步无法打印路径的情况,需要手动判断一下
							ans.append("D");
						}else if(temp.y+1 == m) {
							ans.append("R");
						}
						while(temp!=null) {
							ans.append(temp.dir);
							step++;
							temp = temp.pre;
						}
						System.out.println(step);
						System.out.println(ans.reverse().toString());
						return;
					}
					book[x][y] = 1;//此时标记已经访问
				}
			}
			Q.poll();
		}
		
	}

解法二:DFS+路径存储
这道也可以用DFS来解决,但是逻辑相对较混乱,AC之后也只有30分,自己不是vip也看不到评测数据,感觉自己测的时候没啥问题啊(* 0 *),有点迷茫

/***
	 * 
	 * @param map 地图
	 * @param n 地图的长
	 * @param m 地图的宽
	 */
	static void DFS(int[][] map,int n,int m) {
		/*
		 * 方向数组
		 */
		int[][] dis = {
				{-1,0},//上
				{0,1},//右
				{0,-1},//左
				{1,0},//下
		};
		/*
		 * 路径输出数组,注意此时要按字典序从大到小遍历顺序来排,因为栈是后进先出的,越早入栈越晚遍历到
		 */
		String[] str = {"U","R","L","D"};
		int[][] book = new int[n+1][m+1];
		Stack<N> S = new Stack<N>();
		N begin = new N(1,1,"",null);
		S.push(begin);//把迷宫入口推入栈中,并标记已经访问
		book[1][1]=1; 
		while(!S.isEmpty()) {//如果栈不为空,还可以继续遍历
			N p = S.pop();//弹出第一个元素
			for(int i=0;i<4;i++) {
				//枚举四个方向
				int x = p.x+dis[i][0];
				int y = p.y+dis[i][1];
				//边界判断
				if(x<1 || x>n || y<1 || y>m || map[x][y]==1 || book[x][y]==1) {
					continue;
				}
				if(map[x][y] == 0 && book[x][y] == 0) {
					if(x==n && y==m) {
						//终点判断
						S.push(new N(x,y,str[i],p));
						N tail = S.peek();
						StringBuffer ans = new StringBuffer();//依旧是append后反转一下输出
						int t = 0;
						while(tail.pre!=null) {
							ans.append(tail.dir);
							t++;
							tail = tail.pre;
						}
						System.out.println(t);
						System.out.println(ans.reverse().toString());
						return;
					}
					S.push(new N(x,y,str[i],p));
					book[x][y] = 1;
				}
			}
		}
	}

Main函数

public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		int[][] map = new int[n+1][m+1];
		for(int i=1;i<=n;i++) {
			String st = in.next();
			for(int j=1;j<=m;j++) {
				map[i][j] = Integer.parseInt( (String.valueOf(st.charAt(j-1))));//这个要强转一下,字符转成整数
			}
		}
		DFS(map, n, m);
		BFS(map, n, m);
	}

这道题花了我很长的时间来思考,难点在于,这道题是广搜的升级版,考虑到了路径存储,既要保证路径正确不能走错,还要考虑字典序输出,算法还可待优化,希望有大佬可指教。

猜你喜欢

转载自blog.csdn.net/qq_37756310/article/details/88726326