【BFS】华子20230506笔试第三题(动态迷宫问题)Java实现

文章介绍了如何使用BFS算法解决一个特殊的迷宫问题,其中迷宫地图会按周期变化,且包含动态和静态障碍。解决方案是通过建立三维邻接矩阵存储不同时间点的迷宫状态,并扩展传统的BFS搜索,考虑原地不动的情况,直到所有可能的路径都被探索完。
摘要由CSDN通过智能技术生成

题目链接

塔子哥的codeFun2000:http://101.43.147.120/p/P1251
在这里插入图片描述

测试样例1
输入

3
2
1 0 1 2
2 1 2 0
100 100 100
100 000 100
000 000 001

输出

1

测试样例2
输入

3
2
1 0 2 0
0 1 2 2
000 000 001
010 101 101
110 010 000

输出

5

解释:最快的移动顺序: [2,2] ->[1,2] ->[2,2] ->[2,1] ->[1,1] ->[0,1]

思路

本题属于迷宫类最短路径的问题,首先想到的是BFS,但与常规的迷宫问题不同,该题目中迷宫地图为动态的,障碍物(墙壁)会发生改变,其变化存在周期性(周期为3,对应三个时间点的地图)。且题目中冒险家可选择原地不动,即每次移动有上、下、左、右、原地不动共5种情况。另外,题目中需求解最短时间与常规BFS求解最短路径长度没有本质区别。

综上思考,结合原题,代码思路总结为以下几点:

  • 直接套用BFS板子。
  • 将迷宫存储为动态,用三维邻接矩阵存储(第一维为时间);同时地图中还有不随时间改变的陷阱,即存在动态障碍静态障碍
  • 每次移动共有5种情况。
  • 在常规BFS中的用于记录(x,y)是否走过的boolearn visited数组,在本题中改为三维,即对应三个时间点的某个位置(x,y)是否走过。
  • BFS的队列中存储每个点及到达该点的时间,每个点为(x,y,t)。
  • 因地图以三个单位时间循环,也就说如果地图中的所有可达点对应的三个时刻均被走过,则题目无解。

BFS板子

BFS模板:While循环+队列

分支限界法(BFS、广度优先搜索):每一个活结点只有一次机会成为扩展结点,一旦活结点成为了扩展结点,就一次性产生其所有的儿子结点。其目标为尽快找出满足约束条件的一个解。

// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
    Queue<Node> q; // 核心数据结构
    Set<Node> visited; // 用于记录某个点是否走过,避免走回头路,在二维矩阵迷宫常用boolean[][]记录某个点x,y是否走过
    
    q.offer(start); // 将起点加入队列
    visited.add(start);
    int step = 0; // 记录扩散的步数

    while (q not empty) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散 */
        for (int i = 0; i < sz; i++) { 
            Node cur = q.poll();
            /* 划重点:这里判断是否到达终点 */
            if (cur is target)
                return step;
            /* 将 cur 的相邻节点加入队列 */
            for (Node x : cur.adj()) { // 一旦活结点成为了扩展结点,就一次性产生其所有的儿子结点。
                if (x not in visited) {
                    q.offer(x);
                    visited.add(x);
                }
            }
        }
        /* 划重点:更新步数在这里 */
        step++;
    }
}

以上模板来自东哥总结,地址:labuladong

我的解答

import java.util.*;

/**
 * @program: 
 * @ClassName Main
 * @description: 
 * @author: wangzp
 * @create: 2023-05-06
 * @Version 1.0x
 **/
public class Main {

    static int N;
    static int K;

    static Set<String> guaiwuset = new HashSet<>(); // 用于存储地图中的静态障碍

    static int row1,col1; // 公主(宝藏)
    static int row2,col2; // 王子(冒险家)

    static Queue<int[]> queue; //BFS的队列中存储每个点及到达该点的时间,每个点为(x,y,t)

    static int[][][] graph;// 三维的链接矩阵矩阵存储动态地图

    public static void main(String[] args) {
        // 1.处理输入
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        K = scanner.nextInt();
        graph = new int[3][N][N]; // 0 1 2 共三个时间点的地图
        for (int i = 0; i < K; i++) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            guaiwuset.add(""+x+"-"+y);
        }
        row1 = scanner.nextInt();
        col1 = scanner.nextInt();
        row2 = scanner.nextInt();
        col2 = scanner.nextInt();

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                String str = scanner.next();
                graph[0][i][j] = Integer.valueOf(Character.toString(str.charAt(0)));
                graph[1][i][j] = Integer.valueOf(Character.toString(str.charAt(1)));
                graph[2][i][j] = Integer.valueOf(Character.toString(str.charAt(2)));
            }
        }
        // 2.解答开始
        queue = new LinkedList<>(); // BFS的队列
        boolean[][][] visitedSum = new boolean[N][N][3]; // 记录某个时间点的某个位置是否`走过`

        queue.add(new int[]{row2,col2,0});  // 将初始节点放入队列
        visitedSum[row2][col2][0] = true;   // 将初始节点的0时刻标记为`走过`

        int time = 0;// 记录时间
        while (!queue.isEmpty()){
            int q_size = queue.size();

            for (int i = 0; i < q_size; i++) {
                int[] cur = queue.poll();
                int curx = cur[0];
                int cury = cur[1];
                int curtime = cur[2];
                //System.out.println("x="+curx+" y="+cury+" curtime="+curtime);

                if(curx == row1 && cury == col1){ // 找到最终目标,直接返回答案
                    System.out.println(curtime);
                    return;
                }
                int[][] fangxiang = new int[][]{{0,-1},{0,1},{-1,0},{1,0},{0,0}}; // 上下左右及原地不动5种选择
                for(int[] f:fangxiang){
                    int newx = curx + f[0];
                    int newy = cury + f[1];
                    int newtime = curtime+1;
                    // 判断越界
                    if(newx<0||newx>=N||newy>=N||newy<0)continue;
                    // 判断静态障碍
                    if(guaiwuset.contains(""+newx+"-"+newy)){ // 有怪兽(陷阱)
                        continue;
                    }
                    // 判断动态障碍
                    if(graph[newtime%3][newx][newy]==1){ // 下个节点有障碍
                        continue;
                    }
                    // 判断是否走过
                    if(visitedSum[newx][newy][newtime%3] == true){
                        continue;
                    }
                    queue.offer(new int[]{newx,newy,newtime});
                    visitedSum[newx][newy][newtime%3] = true;
                }
            }
        }
        System.out.println("-1");
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值