28我好想逃却逃不掉-青训营刷题

问题描述

曾经的我不过是一介草民,混迹市井,默默无名。直到我被罗马的士兵从家乡捉走丢进竞技场……

对手出现了,我架紧盾牌想要防御,只觉得巨大的冲击力有如一面城墙冲涌而来,击碎了我的盾牌,我两眼发昏,沉重的身躯轰然倒地。

——我好想逃。

但罗马最大的竞技场,哪有这么容易逃得掉。工程师们早就在地上装了传送机关,虽不会伤人,却会将站在上面的人传到它指向的位置。若是几个传送机关围成一个环,不小心踩在上面的人就会被一圈圈地反复传送……想到这里,我不由得打了个寒颤。必须避开这些危险的地方!

在一个 N × M 的竞技场迷宫中,你的任务是找出在迷宫中,所有"危险位置"的数量。
"危险位置"定义为:如果站在该位置上,无论采取什么移动策略,都无法到达出口

竞技场中包含以下几种元素:

  • .:表示普通地板,可以自由移动到上下左右相邻的格子(不可以走斜线)
  • O:表示出口
  • U:表示向上的传送器,踩上去会被强制传送到上方的格子
  • D:表示向下的传送器,踩上去会被强制传送到下方的格子
  • L:表示向左的传送器,踩上去会被强制传送到左方的格子
  • R:表示向右的传送器,踩上去会被强制传送到右方的格子

注意,如果被传送出了竞技场之外,则算作死亡。


输入参数

  • N: 一个整数,表示竞技场地图的行数
  • M: 一个整数,表示竞技场地图的列数
  • data: 一个字符二维数组,表示竞技场地板地图。数组大小为 N × M,其中 1 ≤ N, M ≤ 100

测试样例

样例1:

输入:N = 5, M = 5, data = [ [".", ".", ".", ".", "."], [".", "R", "R", "D", "."], [".", "U", ".", "D", "R"], [".", "U", "L", "L", "."], [".", ".", ".", ".", "O"] ]
输出:10
解释:存在 10 个位置,如果站在这些位置上,将永远无法到达右下角的出口(用 X 标记):
['.', '.', '.', '.', '.']
['.', 'X', 'X', 'X', '.']
['.', 'X', 'X', 'X', 'X']
['.', 'X', 'X', 'X', '.']
['.', '.', '.', '.', 'O']

样例2:

输入:N = 4, M = 4, data = [[".", "R", ".", "O"], ["U", ".", "L", "."], [".", "D", ".", "."], [".", ".", "R", "D"]]
输出:2

样例3:

输入:N = 3, M = 3, data = [[".", "U", "O"], ["L", ".", "R"], ["D", ".", "."]]
输出:8

代码如下: 

import java.util.*;
 
public class Main {
 
    public static int solution(int N, int M, char[][] data) {
        boolean[][] visited = new boolean[N][M];
        // 定义方向数组,用于处理普通移动
        Map directions = new HashMap<>();
        directions.put('U', new int[]{-1, 0});
        directions.put('D', new int[]{1, 0});
        directions.put('L', new int[]{0, -1});
        directions.put('R', new int[]{0, 1});
 
        // 找到出口位置
        Queue queue = new LinkedList<>();
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (data[i][j] == 'O') {
                    queue.add(new int[]{i, j});
                    visited[i][j] = true;
                }
            }
        }
 
        // 广度优先搜索
        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int x = current[0];
            int y = current[1];
 
            // 遍历四个方向
            for (Map.Entry entry : directions.entrySet()) {
                char direction = entry.getKey();
                int[] delta = entry.getValue();
                int dx = delta[0], dy = delta[1];
                int nx = x + dx, ny = y + dy;
 
                // 检查是否在边界内
                if (nx >= 0 && nx < N && ny >= 0 && ny < M) {
                    // 是否是传送器
                    if (directions.containsKey(data[nx][ny])) {
                        // 处理传送器
                        int tx = nx, ty = ny;
                        boolean reverse = false;
                        while (directions.containsKey(data[tx][ty])) {
                            int[] move = directions.get(data[tx][ty]);
                            tx += move[0];
                            ty += move[1];
                            if (tx < 0 || tx >= N || ty < 0 || ty >= M || (tx == nx && ty == ny)) {
                                reverse = true;
                                break;
                            }
                        }
 
                        if (tx >= 0 && tx < N && ty >= 0 && ty < M && visited[tx][ty] && !reverse) {
                            queue.add(new int[]{nx, ny});
                            visited[nx][ny] = true;
                        }
                    } else if (!visited[nx][ny] && data[nx][ny] != '#') {
                        queue.add(new int[]{nx, ny});
                        visited[nx][ny] = true;
                    }
                }
            }
        }
 
        // 统计危险位置数量
        int dangerCount = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (!visited[i][j] && data[i][j] != '#') {
                    dangerCount++;
                }
            }
        }
 
        return dangerCount;
    }
 
    public static void main(String[] args) {
        // 示例测试
        char[][] pattern = {
            {'.', '.', '.', '.', '.'},
            {'.', 'R', 'R', 'D', '.'},
            {'.', 'U', '.', 'D', 'R'},
            {'.', 'U', 'L', 'L', '.'},
            {'.', '.', '.', '.', 'O'}
        };
        System.out.println(solution(5, 5, pattern));  // 输出结果
    }
}

 代码解释:

这段代码是一个基于广度优先搜索(BFS)的算法,用于计算在一个二维网格中,从出口位置出发无法到达的“危险区域”的数量。以下是代码的详细解释:

1. 主要功能

  • 输入:一个二维字符数组 data,表示网格地图,其中包含以下几种字符:

    • '.':普通区域,可以通行。

    • 'O':出口位置。

    • '#:障碍物,无法通行。

    • 'U''D''L''R':传送器,分别表示向上、向下、向左、向右传送。

  • 输出:无法从出口位置到达的“危险区域”数量。

2. 代码逻辑

2.1 初始化
  • 方向数组:使用 HashMap 存储四个方向的移动规则,键为方向字符,值为对应的坐标变化。

  • 访问标记数组visited 用于记录每个位置是否被访问过,防止重复访问。

  • 队列:用于广度优先搜索,初始时将所有出口位置加入队列。

2.2 广度优先搜索(BFS)
  • 队列操作:每次从队列中取出一个位置 (x, y),尝试向四个方向移动。

  • 普通移动:如果目标位置在网格范围内且未被访问过,并且不是障碍物,则将其加入队列并标记为已访问。

  • 传送器处理

    • 如果目标位置是传送器('U''D''L''R'),则根据传送方向继续移动,直到遇到非传送器位置或超出网格范围。

    • 如果传送过程中出现循环(即传送后回到原位置),则跳过该位置。

    • 如果传送后到达的位置未被访问过,则将其加入队列并标记为已访问。

2.3 统计危险区域
  • 遍历整个网格,统计未被访问且不是障碍物的位置数量,这些位置即为“危险区域”。

3. 代码中的关键点

  • 方向数组的使用:通过 HashMap 存储方向和对应的坐标变化,方便在搜索过程中快速获取移动方向。

  • 传送器的处理:通过循环处理传送器的连续移动,确保能够正确处理复杂的传送路径。

  • 边界条件检查:在每次移动时,都检查目标位置是否在网格范围内,避免数组越界。

4. 示例运行

以代码中的测试数据为例:

java复制

char[][] pattern = {
    {'.', '.', '.', '.', '.'},
    {'.', 'R', 'R', 'D', '.'},
    {'.', 'U', '.', 'D', 'R'},
    {'.', 'U', 'L', 'L', '.'},
    {'.', '.', '.', '.', 'O'}
};
  • 出口位置为 (4, 4)

  • 从出口位置开始,通过 BFS 搜索可以到达的所有位置。

  • 最终统计未被访问的非障碍物位置数量,即为“危险区域”的数量。

5. 输出结果

对于上述测试数据,程序输出的“危险区域”数量为 6

6. 代码优化建议

  • 效率优化:在处理传送器时,可以通过缓存已处理的传送路径来减少重复计算。

  • 代码可读性:增加注释,说明每个步骤的具体逻辑,便于理解和维护。

这段代码通过广度优先搜索和方向数组,有效地解决了从出口位置出发的可达性问题,并统计了无法到达的区域数量。

你可能感兴趣的:(算法,java,数据结构)