我好想逃却逃不掉
问题描述
曾经的我不过是一介草民,混迹市井,默默无名。直到我被罗马的士兵从家乡捉走丢进竞技场……
对手出现了,我架紧盾牌想要防御,只觉得巨大的冲击力有如一面城墙冲涌而来,击碎了我的盾牌,我两眼发昏,沉重的身躯轰然倒地。
——我好想逃。
但罗马最大的竞技场,哪有这么容易逃得掉。工程师们早就在地上装了传送机关,虽不会伤人,却会将站在上面的人传到它指向的位置。若是几个传送机关围成一个环,不小心踩在上面的人就会被一圈圈地反复传送……想到这里,我不由得打了个寒颤。必须避开这些危险的地方!
在一个 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
思路
通过问题不难发现,如果一个位置可以到达出口,那么出口同样可以到达这一个位置,所以只要统计从出口位置开始,能够到达的位置,就可以得到那些位置不可达。所以可以考虑从出口开始使用DFS或者BPS遍历迷宫的每一个位置。DFS可以使用递归来计算,BPS可以构造一个point类,通过创建一个队列来遍历。
-
找到出口位置:
- 遍历整个迷宫,找到出口
'O'的位置,并记录其坐标(exitX, exitY)。
- 遍历整个迷宫,找到出口
-
从出口开始反向DFS:
- 从出口位置开始,使用深度优先搜索(DFS)反向标记所有可以到达出口的位置。
- 在DFS过程中,检查当前位置的上下左右四个方向,如果某个方向上的位置可以通过传送器或者普通地板到达当前位置,则继续DFS该位置。
-
统计无法到达出口的位置:
- 遍历整个迷宫,统计所有
canWalk为false的位置,这些位置即为无法到达出口的"危险位置"。
- 遍历整个迷宫,统计所有
代码
public class Main {
static boolean[][] visited;
public static int solution(int N, int M, char[][] data) {
int exitX = 0, exitY = 0;
boolean foundExit = false;
// 找到迷宫出口
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (data[i][j] == 'O') {
exitX = i;
exitY = j;
foundExit = true;
break;
}
}
if (foundExit) break;
}
visited = new boolean[N][M];
boolean[][] canWalk = new boolean[N][M];
canWalk[exitX][exitY] = true; // 标记出口为可达
dfs(N, M, data, exitX, exitY, canWalk);
// 统计无法到达出口的位置数量
int unreachableCount = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!canWalk[i][j]) {
unreachableCount++;
}
}
}
return unreachableCount;
}
public static void dfs(int N, int M, char[][] data, int x, int y, boolean[][] canWalk) {
//显示二维结果数组
// System.out.println();
// for (int i = 0; i < N; i++) {
// for (int j = 0; j < M; j++) {
// if (canWalk[i][j]){
// System.out.print(1 + " ");
// }else{
// System.out.print(0 + " ");
// }
// }
// System.out.println();
// }
//判断是否在迷宫内和访问过
if (x < 0 || x >= N || y < 0 || y >= M) return; // 边界条件
if (visited[x][y]) return;
visited[x][y] = true;
// 标记当前位置为可到达
canWalk[x][y] = true;
// 向上检查
if (x - 1 >= 0 && (data[x - 1][y] == 'D' || data[x - 1][y] == '.')) {
dfs(N, M, data, x - 1, y, canWalk);
}
// 向下检查
if (x + 1 < N && (data[x + 1][y] == 'U' || data[x + 1][y] == '.')) {
dfs(N, M, data, x + 1, y, canWalk);
}
// 向左检查
if (y - 1 >= 0 && (data[x][y - 1] == 'R' || data[x][y - 1] == '.')) {
dfs(N, M, data, x, y - 1, canWalk);
}
// 向右检查
if (y + 1 < M && (data[x][y + 1] == 'L' || data[x][y + 1] == '.')) {
dfs(N, M, data, x, y + 1, canWalk);
}
}
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) == 10);
}
}