迷宫(dfs与bfs)

1. 问题描述:

【问题描述】
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这个它的上、下、左、右四个方向之一。对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,一共 10 步。其中 D、 U、 L、 R 分别表示向下、向上、向左、向右走。对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。请注意在字典序中D<L<R<U。(如果你把以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 maze.txt,内容与下面的文本相同)

01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个字符串,包含四种字母 D、 U、 L、 R,在提交答案时只填写这个字符串,填写多余的内容将无法得分

这道题目为:第十届蓝桥杯大赛软件类省赛 Java 大学 B 组

2. 思路分析

① 这道题目是一道经典的迷宫问题,一般来说迷宫问题是要求解出最短路径的步数是多少什么的,可以使用深度优先搜索来解决,也即dfs,因为dfs可以搜索每一条可能的路径,在走迷宫的过程中可以往上下左右这四个方向进行移动,当尝试这一条路径往下走的时候走不通了需要进行回溯也就是再调用方法之后将状态恢复到调用之前的状态,这里可以使用一个二维数组来进行标记之前走过的格子,当调用完当前的方法之后将该标记数组恢复到之前的未访问的状态,这样再尝试另外一条路径的时候才可以尝试走之前走过的格子,所以使用dfs来解决需要注意以下几个问题:

1)使用if判断来进行剪枝,在判断中可以决定是否可以往当前的方向走,同时也防止数组越界的问题,因为可以走四个方向,当走完这个方向的时候可以尝试走另外一个方向,所以对应着四个平行状态

2)因为二维数组不像一维数组,一维数组不需要标记节点是否已经被访问过,而二维数组往下走的过程中可能会重新走到与之前相同的地方,假如没有标记的话那么可能会走之前已经走过的地方这样的话会一直递归调用出不来,所以需要使用一个数据结构来进行记录,这里的话使用的就是二维数组,在dfs方法一开始调用的时候就可以进行记录,因为每调用一次那么这个节点就被访问了那么就可以标记已访问(这个是千万要记住运行的时候内存溢出有可能是这个情况)

3)需要进行回溯,因为尝试走一条路径的时候有可能走不通,当退回来的时候需要将之前已经访问过的标记为未访问,恢复到之前的状态,这里恢复的就是二维数组的记录的状态,这样在尝试走下一个方向的时候才不会受到影响

因为题目的给出的迷宫太大了,在使用idea运行的时候直接就内存溢出报错了,小一点的迷宫才可以运行出来,但是对于代码来说还是有一定的理解价值的,理解一下dfs需要注意的点即可

下面使用bfs来解决,在使用bfs来解决的时候发现还是可以的,可以求解出结果而且运行速度还是比较快的

② bfs,也就是宽度优先搜索

宽度优先搜索借助于队列来实现,首先队列加入起点,然后判断周围的邻居是否满足条件,假如满足条件那么加入周围的邻居节点,这样就可以向周围的节点进行扩散,通往其他的节点,需要注意以下几个问题:

1)与dfs一样,bfs也是需要一个二维数组来标记已经访问过的地方,保证加入对的节点是之前没有被访问过的,当弹出当前节点的时候就可以标记当前的节点已被访问

2)因为涉及到迷宫的行数与列数所以使用一个内部类来进行封装,类中包含着当前节点的行与列的信息,并且需要记录到达当前这个节点所经过的路径

3)bfs只会求解出一条最短的路径,对于这道题目来说可能是只有一个最短路径的答案所以应该是正确的,因为bfs求解迷宫的时候到达了出口只会不会像dfs那样进行回溯,所以当求解出一条最短路径之后那么其他的路径可能就求解不出来了,因为是选择题所以我们可以调整代码中四个方向的位置来进行验证这样的话可以求解字典序最小的那个

3. 代码如下:

① dfs代码如下

import java.util.*;
public class Main {
    static int row, col;
    static int vis[][];
    static TreeSet<String> set = new TreeSet<>();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        row = sc.nextInt();
        col = sc.nextInt();
        vis = new int[row][col];
        char arr[][] = new char[row][col];
        for (int i = 0; i < row; i++){
            arr[i] = sc.next().toCharArray();
        }
        /*for (int i = 0; i < row; i++){
            System.out.println(arr[i]);
        }*/
        dfs(arr, 0, 0, "");
        /*如何遍历set集合*/
        Iterator<String> it = set.iterator();
        /*不能够使用for循环*/
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }

    /*dfs
    * 之前很久之前没有写过dfs导致在写的时候忘记了二维数组需要记录已经访问过的点否则会造成堆栈溢出的情况
    * 还有一个很重要的问题是需要进行回溯要不然只有一种答案了而且记录的是错误的路径包含着不需要的路径
    * 假如迷宫太大会造成内存溢出对于idea的话迷宫太大了
    *
    * */
    private static void dfs(char[][] arr, int r, int c, String rec) {
        /*标记当前已经访问的点*/
        vis[r][c] = 1;
        if (r == row - 1 && c == col - 1) {
            set.add(rec);
            return;
        }
        /*使用if进行减枝当满足条件的时候才往下进行调用
        * 四个平行状态
        * */
        /*往右走*/
        /*System.out.println(r + " " + c);*/
        if (c + 1 < col && arr[r][c + 1] == '0' && vis[r][c + 1] == 0){
            dfs(arr, r, c + 1, rec + "R");
            vis[r][c + 1] = 0;
        }
        /*往下走*/
        if (r + 1 < row &&  arr[r + 1][c]== '0' && vis[r + 1][c] == 0){
            dfs(arr, r + 1, c, rec + "D");
            vis[r + 1][c] = 0;
        }
        /*往上走*/
        if (r - 1 >= 0 && arr[r - 1][c] == '0' && vis[r - 1][c] == 0){
            dfs(arr, r - 1, c, rec + "U");
            vis[r - 1][c] = 0;
        }
        /*往左走*/
        if (c - 1 >= 0 && arr[r][c - 1] == '0' && vis[r][c - 1] == 0){
            dfs(arr, r, c - 1, rec + "L");
            vis[r][c - 1] = 0;
        }
    }
}

② 因为在走最短路径的过程的可以使用节点中的字符串来记录,所以这个给后面写最短的迷宫路径提供了思路,bfs代码如下:

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
    static int row, col;
    static char arr[][];
    static int rec[][];
    static String res = "";
    static Queue<Node> queue = new LinkedList<>();
    /*表示向四个方向进行移动: 下 上 右 左 */
    static int dr[] = {1, -1, 0, 0};
    static int dc[] = {0, 0, 1, -1};
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        row = sc.nextInt();
        col = sc.nextInt();
        arr = new char[row][col];
        rec = new int[row][col];
        for (int i = 0; i < row; ++i){
            /*接收迷宫*/
            arr[i] = sc.next().toCharArray();
        }
        /*往队列中加入起点*/
        Node start = new Node(0, 0, "");
        queue.add(start);
        System.out.println(bfs());
    }

    private static String bfs() {
        while (!queue.isEmpty()){
            /*弹出节点*/
            Node poll = queue.poll();
            /*System.out.println(poll.getLoc());*/
            int r = poll.getR();
            int c = poll.getC();
            /*这一个非常重要因为加入没有标记的话那么可能会重复访问之前已经访问过的节点这个是与dfs是一样的
            *
            * */
            rec[r][c] = 1;
            if (r == row - 1 && c == col - 1){
                res = poll.getLoc();
            }
            for (int i = 0; i < 4; ++i){
                int nowr = r + dr[i];
                int nowc = c + dc[i];
                if (nowr >= 0 && nowr < row && nowc >= 0 && nowc < col && arr[nowr][nowc] == '0' && rec[nowr][nowc] == 0){
                    Node newNode = new Node(nowr, nowc);
                    if (i == 0){
                        //到达该节点所经历的路径需要加入进来
                        newNode.setLoc(poll.getLoc() + "D");
                    }else if (i == 1){
                        newNode.setLoc(poll.getLoc() + "U");
                    }else if (i == 2){
                        newNode.setLoc(poll.getLoc() + "R");
                    }else if (i == 3){
                        newNode.setLoc(poll.getLoc() + "L");
                    }
                    queue.add(newNode);
                }
            }
        }
        return res;
    }

    /*加入队列的一个节点*/
    public static class Node{
        private int r;
        private int c;
        /*记录其中的路线*/
        private String loc;
        /*构造器*/
        public Node(int r, int c, String loc) {
            this.r = r;
            this.c = c;
            this.loc = loc;
        }

        public Node(int r, int c) {
            this.r = r;
            this.c = c;
        }
        public int getR() {
            return r;
        }

        public void setR(int r) {
            this.r = r;
        }

        public int getC() {
            return c;
        }

        public void setC(int c) {
            this.c = c;
        }

        public String getLoc() {
            return loc;
        }

        public void setLoc(String loc) {
            this.loc = loc;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "r=" + r +
                    ", c=" + c +
                    ", loc='" + loc + '\'' +
                    '}';
        }
    }
}

答案是这个:

DDDDRRURRRRRRRDRRRDDDLDDRDDDDDDDDDDDDRDRDRRURRUURRDDDDRDRRRRRRURRDRRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDRDRRRRDRDRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值