牛客小白月赛99 C-迷宫(DFS)

题目描述

给定一个 n×m\mathrm{n \times m}n×m 的迷宫,迷宫由 "#" 与"." 两种字符组成。其中 "#" 代表障碍物,"." 表示空地。迷宫中还有一个起点 "S" 和一个终点 "E" ,它们都可以视为空地。

由于近期迷宫发生了塌方,导致起点和终点之间可能并不连通。幸运的是,你拥有一种超能力——在迷宫中移动时(移动方向为上、下、左、右四个方向之一),可以在当前位置朝任一方向(上、下、左、右四个方向之一)释放激光。激光能够清除该方向上所有的障碍物,并且这种超能力至多只能使用一次。

现在,你需要判断是否能利用这种超能力成功从起点到达终点。

输入描述:

第一行给定两个整数n,m(2≤n,m≤1000),分别表示迷宫的行数和列数。 下面n行,每行m个字符,描述迷宫的具体布局。字符只包含"#"、"."、"S"和"E",并且起点与终点有且仅有一个。

 

输出描述:

能够到达终点输出YES,否则输出NO。

示例1

输入

4 5
.####
S####
.####
.E###

 

输出

YES

 

示例2

输入

4 5
..###
S####
#####
##.E#

 

输出

YES

 

说明

显然可以从起点出发,到达(1,2)\mathrm{(1,2)}(1,2)处并向下方使用超能力,此时可以从起点到达终点。
示例3

输入

4 5
..###
S####
#####
###E#

 

输出

NO 

 

问题的关键在于可以清除某个方向的障碍物,我一开始想用DFS+回溯去暴力破解,但回溯需要修正的变量过于繁琐且时间复杂度爆炸,只能另取他法。

通过观察可以发现,对起点和终点都进行遍历,设与起点连通的点集为x,与终点连通的点集为y,任取y中一点y1,若在y1的相邻行或列以及本行中存在x中的某点,那就可以通过清除障碍进行连通。

所以释放激光后起点与终点连通 等价于 起点所在连通块与终点所在连通块存在相邻或相同的行/列,因此可以考虑分别从起点与终点进行两遍DFS,从终点出发的DFS标记出其能够直接到达的行列坐标;从起点出发的
DFS枚举每个位置以及释放激光的方向,判断是否可能连通。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m;
 4 int s_x, s_y, e_x, e_y; //起点与终点
 5 bool result = false;
 6 char graph[1005][1005];
 7 int visited[1005][1005]; //访问标记,1代表被正向访问,2代表被反向访问,正向表示从起点出发,反向表示从终点出发
 8 bool row[1005],column[1005]; //row[i]表示第i行是否有正向连通点,column[i]表示第i列是否有正向连通点
 9 
10 void dfs(int x, int y, int orientation){
11     visited[x][y] = orientation;
12     if (orientation == 1){
13         row[x] = 1;
14         column[y] = 1;
15     }
16     if (x == e_x && y == e_y && orientation == 1){
17         result = true;
18         return;
19     }
20     if (orientation == 2){ //进入反向遍历说明起点与终点不连通
21         for (int i = x - 1; i <= x + 1; ++i){ //直接判断附近是否有正向连通点
22             if (i > 0 && i <= n && row[i]){
23                 result = true;
24                 return;
25             }
26 
27         }
28         for(int j = y - 1; j <= y + 1; ++j){
29             if (j > 0 && j <= m && column[j]){
30                 result = true;
31                 return;
32             }
33         }
34     }
35     if (visited[x - 1][y] == 0 && graph[x - 1][y] != '#'){
36         dfs(x - 1, y, orientation);
37     }
38     if (visited[x + 1][y] == 0 && graph[x + 1][y] != '#'){
39         dfs(x + 1, y, orientation);
40     }
41     if (visited[x][y - 1] == 0 && graph[x][y - 1] != '#'){
42         dfs(x, y - 1, orientation);
43     }
44     if (visited[x][y + 1] == 0 && graph[x][y + 1] != '#'){
45         dfs(x, y + 1, orientation);
46     }
47 
48 }
49 int main(){
50     memset(graph, '#', sizeof(graph));
51     memset(visited, 0, sizeof(visited));
52     memset(row, false, sizeof(row));
53     memset(column, false, sizeof(row));
54     cin>>n>>m;
55     for (int i = 1; i <= n; ++i){
56         for (int j = 1; j <= m; ++j){
57             cin>>graph[i][j];
58             if (graph[i][j] == 'S'){
59                 s_x = i; s_y = j;
60             }
61             if (graph[i][j] == 'E'){
62                 e_x = i; e_y = j;
63             }
64         }
65     }
66     dfs(s_x, s_y, 1);
67     if (result){ //起点终点原本就连通
68         cout<<"YES"<<endl;
69         return 0;
70     }
71     dfs(e_x, e_y, 2);
72     if (result)
73         cout<<"YES"<<endl;
74     else
75         cout<<"NO"<<endl;
76 }

 

 其他类似的可以破墙的题目也可以用类似的正反遍历的思路去解,只要确定一下可以连通的条件即可。

posted on 2024-08-24 18:33  Coder何  阅读(99)  评论(0)    收藏  举报