曾经的我不过是一介草民,混迹市井,默默无名。直到我被罗马的士兵从家乡捉走丢进竞技场……
对手出现了,我架紧盾牌想要防御,只觉得巨大的冲击力有如一面城墙冲涌而来,击碎了我的盾牌,我两眼发昏,沉重的身躯轰然倒地。
——我好想逃。
但罗马最大的竞技场,哪有这么容易逃得掉。工程师们早就在地上装了传送机关,虽不会伤人,却会将站在上面的人传到它指向的位置。若是几个传送机关围成一个环,不小心踩在上面的人就会被一圈圈地反复传送……想到这里,我不由得打了个寒颤。必须避开这些危险的地方!
在一个 N × M 的竞技场迷宫中,你的任务是找出在迷宫中,所有"危险位置"的数量。
"危险位置"定义为:如果站在该位置上,无论采取什么移动策略,都无法到达出口。
竞技场中包含以下几种元素:
.
:表示普通地板,可以自由移动到上下左右相邻的格子(不可以走斜线)O
:表示出口U
:表示向上的传送器,踩上去会被强制传送到上方的格子D
:表示向下的传送器,踩上去会被强制传送到下方的格子L
:表示向左的传送器,踩上去会被强制传送到左方的格子R
:表示向右的传送器,踩上去会被强制传送到右方的格子注意,如果被传送出了竞技场之外,则算作死亡。
样例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)的算法,用于计算在一个二维网格中,从出口位置出发无法到达的“危险区域”的数量。以下是代码的详细解释:
输入:一个二维字符数组 data
,表示网格地图,其中包含以下几种字符:
'.'
:普通区域,可以通行。
'O'
:出口位置。
'#
:障碍物,无法通行。
'U'
、'D'
、'L'
、'R'
:传送器,分别表示向上、向下、向左、向右传送。
输出:无法从出口位置到达的“危险区域”数量。
方向数组:使用 HashMap
存储四个方向的移动规则,键为方向字符,值为对应的坐标变化。
访问标记数组:visited
用于记录每个位置是否被访问过,防止重复访问。
队列:用于广度优先搜索,初始时将所有出口位置加入队列。
队列操作:每次从队列中取出一个位置 (x, y)
,尝试向四个方向移动。
普通移动:如果目标位置在网格范围内且未被访问过,并且不是障碍物,则将其加入队列并标记为已访问。
传送器处理:
如果目标位置是传送器('U'
、'D'
、'L'
、'R'
),则根据传送方向继续移动,直到遇到非传送器位置或超出网格范围。
如果传送过程中出现循环(即传送后回到原位置),则跳过该位置。
如果传送后到达的位置未被访问过,则将其加入队列并标记为已访问。
遍历整个网格,统计未被访问且不是障碍物的位置数量,这些位置即为“危险区域”。
方向数组的使用:通过 HashMap
存储方向和对应的坐标变化,方便在搜索过程中快速获取移动方向。
传送器的处理:通过循环处理传送器的连续移动,确保能够正确处理复杂的传送路径。
边界条件检查:在每次移动时,都检查目标位置是否在网格范围内,避免数组越界。
以代码中的测试数据为例:
java复制
char[][] pattern = {
{'.', '.', '.', '.', '.'},
{'.', 'R', 'R', 'D', '.'},
{'.', 'U', '.', 'D', 'R'},
{'.', 'U', 'L', 'L', '.'},
{'.', '.', '.', '.', 'O'}
};
出口位置为 (4, 4)
。
从出口位置开始,通过 BFS 搜索可以到达的所有位置。
最终统计未被访问的非障碍物位置数量,即为“危险区域”的数量。
对于上述测试数据,程序输出的“危险区域”数量为 6
。
效率优化:在处理传送器时,可以通过缓存已处理的传送路径来减少重复计算。
代码可读性:增加注释,说明每个步骤的具体逻辑,便于理解和维护。
这段代码通过广度优先搜索和方向数组,有效地解决了从出口位置出发的可达性问题,并统计了无法到达的区域数量。