牛客小白月赛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 }
其他类似的可以破墙的题目也可以用类似的正反遍历的思路去解,只要确定一下可以连通的条件即可。
浙公网安备 33010602011771号