我好想逃却逃不掉 | 豆包MarsCode AI刷题

8 阅读5分钟

问题描述

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

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

——我好想逃。

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

在一个 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

解题思路如下:

  1. 初始化参数:定义方向映射数组<font style="color:rgb(26, 32, 41);">directions</font>和方向变化数组<font style="color:rgb(26, 32, 41);">directionDeltas</font>,这些将用于确定传送点的移动方向。
  2. 遍历迷宫:使用双层循环遍历迷宫的每个位置(除了出口本身)。对于每个位置,我们需要判断是否能够到达出口。
  3. 检查有效性:定义一个<font style="color:rgb(26, 32, 41);">isValid</font>方法来检查给定的坐标是否在迷宫的有效范围内。
  4. 深度优先搜索(DFS):定义一个<font style="color:rgb(26, 32, 41);">canReachExit</font>方法,使用递归的方式进行深度优先搜索,以检查从当前坐标是否可以到达出口。
  5. 处理传送点:如果当前坐标是传送点,根据传送点的方向直接移动到下一个位置,并继续递归检查。
  6. 探索普通点:如果当前坐标是普通点,尝试所有四个可能的方向(上、下、左、右),递归地检查每个方向是否可以到达出口。
  7. 记录访问状态:使用一个<font style="color:rgb(26, 32, 41);">HashSet</font>来记录已经访问过的位置,以避免重复访问和无限循环。
  8. 统计无法到达出口的位置:如果在递归过程中,从某个位置出发无法到达出口,则将该位置计入无法到达出口的位置总数。
  9. 返回结果:遍历完成后,返回无法到达出口的位置总数。

算法实现步骤:

  1. 遍历迷宫的每个位置。
  2. 对于每个位置,如果它不是出口,使用深度优先搜索(DFS)来检查是否可以从该位置到达出口。
  3. 使用一个集合来记录已经访问过的位置,以避免无限循环。
  4. 如果位置是传送点,直接按照传送点的方向移动到下一个位置,并继续检查。
  5. 如果位置是普通点,尝试所有四个方向(上、下、左、右)。
  6. 如果从某个位置出发无法到达出口,则计数器加一。
  7. 最后,返回计数器的值,即无法到达出口的位置数量。
import java.util.*;

public class Main {
    // 方向映射
    private final static String[] directions = { "U", "D", "L", "R" };
    private final static int[][] directionDeltas = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };

    public static int solution(int N, int M, char[][] data) {
        // 统计无法到达出口的位置数量
        int count = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (data[i][j] != 'O') { // 不考虑出口本身
                    if (!canReachExit(N, M, i, j, new HashSet<>(), data)) {
                        count++;
                    }
                }
            }
        }

        return count;
    }

    // 检查位置是否有效
    public static boolean isValid(int N, int M, int x, int y) {
        return x >= 0 && x < N && y >= 0 && y < M;
    }

    // 使用递归检查从给定位置是否可以到达出口
    public static boolean canReachExit(int N, int M, int start_x, int start_y, Set<String> visited, char[][] data) {
        if (!isValid(N, M, start_x, start_y)) {
            return false;
        }
        String key = start_x + "," + start_y;
        if (visited.contains(key)) {
            return false;
        }
        if (data[start_x][start_y] == 'O') {
            return true;
        }
        visited.add(key);

        // 如果是传送点
        for (int i = 0; i < directions.length; i++) {
            if (data[start_x][start_y] == directions[i].charAt(0)) {
                int dx = directionDeltas[i][0];
                int dy = directionDeltas[i][1];
                return canReachExit(N, M, start_x + dx, start_y + dy, visited, data);
            }
        }

        // 如果是普通点,尝试四个方向
        for (int[] delta : directionDeltas) {
            int next_x = start_x + delta[0];
            int next_y = start_y + delta[1];
            if (isValid(N, M, next_x, next_y) && canReachExit(N, M, next_x, next_y, new HashSet<>(visited), data)) {
                return true;
            }
        }

        return false;
    }

    public static void main(String[] args) {
        // Add your test cases here
        char[][] pattern = {
                { '.', '.', '.', '.', '.' },
                { '.', 'R', 'R', 'D', '.' },
                { '.', 'U', '.', 'D', 'R' },
                { '.', 'U', 'L', 'L', '.' },
                { '.', '.', '.', '.', 'O' }
        };
        System.out.println(solution(5, 5, pattern) == 10);
    }
}