问题描述
小蓝有一天误入了一个混境之地。
好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:
- 混境之地的大小为 n⋅m,其中
#
表示不可通过的墙壁,.
表示可以走的路。 - 他现在所在位置的坐标为 (A,B) ,而这个混境之地出口的坐标为 (C,D) ,当站在出口时即表示可以逃离混境之地。
- 有一句神奇的咒语可以在混境之地中使用,可以击破一面墙壁,即将
#
变为.
。
坏消息是:神奇的咒语副作用很大,会导致使用者身心俱疲,所以最多只能使用一次。
小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,则输入 Yes
,反之输出 No
。
输入格式
第 1 行输入两个正整数 n,m ,表示混境之地的大小。
第 2 行输入四个正整数 A,B,C,D ,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。
第 3 行至第 n+2 行,每行 m 个字符,表示混境之地的地图,其中 #
表示不可通过的墙壁, .
表示普通的道路。
输出格式
输出数据共一行为一个字符串:
- 若小蓝可以逃离混境之地,则输出
Yes
。 - 若小蓝无法逃离混境之地,则输出
No
。
样例输入1
5 5
1 1 5 5
...#.
..#..
#...#
...#.
...#.
样例输出1
Yes
样例解释1
如图所示,绿色方块表示可以走的路,红色方块表示墙,蓝色圆圈小蓝当前所在的位置,橙色圆圈为终点。
从 (1,1) 到 (5,5) 的一条可行道路为: (1,1)→(1,2)→(2,2)→(3,2)→(4,2)→(5,2)→(5,3)→ 打破 (5,4)→(5,5) 。
样例输入2
5 5
1 1 5 5
...#.
..#..
#...#
...##
..##.
样例输出2
No
样例解释2
如图所示,绿色方块表示可以走的路,红色方块表示墙,蓝色圆圈小蓝当前所在的位置,橙色圆圈为传送门 2 。
可以证明,无法通过打破最多一个方块,从左上角到达右下角。
数据范围
对于所有测试样例,1≤n,m≤1000 。
数据保证起点和终点均不为墙。
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 2s | 512M |
C | 2s | 512M |
Java | 3s | 512M |
Python3 | 4s | 512M |
解题思路(DFS/BFS+染色)
观察题目,我们发现,逃离混镜之地仅需满足以下两个条件中的一个:
1. 起点与终点之间存在一条路径,即判断两个点是否连通,那么这个问题,可以使用DFS或者BFS来实现。
2. 打破一个墙壁后,起点与终点之间存在一条合法路径。换言之,起点和终点之间存在一条合法路径被一个墙壁隔开。
对条件2的详细说明:
当我们需要在一个矩阵中找到两个节点之间的路径时,如果只使用DFS或BFS算法,可能会遇到障碍的问题,导致搜索算法无法找到正确的路径。在这种情况下,我们可以使用染色算法来解决这个问题。
染色算法的基本思路是将起点和终点分别染成不同的颜色,然后枚举所有的障碍物,若某个障碍物连接着颜色 1 且连接着颜色 2 那么即表示可以通过破坏该墙壁,使得起点和终点存在一条有效路径。
本题中,我们可以将起点的连通块染成颜色 1,即将起点可以到达的所有点都染色为颜色 1。同理,将终点可以到达的所有点都染色为颜色 2,染色操作可使用DFS或BFS,由于DFS代码量少于BFS,故推荐使用DFS。
在进行DFS过程中,我们需要遍历每个节点的四个相邻节点,如果相邻节点是障碍物,那么我们跳过该节点,反之,染色该节点后遍历该节点的相邻节点,然后继续搜索下一个节点。
代码实现时,我们可以使用一个二维数组来记录每个节点的颜色,若颜色为 0 即代表这点未被染色,为非 0 即代表被染色。
对于墙的判断,我们可以使用二重循环来枚举每个墙壁,然后判断其四周节点的颜色,如果至少有一个颜色为 1,至少有一个颜色为 2,则说明该墙壁连通着最少一条从起点到终点的道路,即破坏该墙壁后,一定存在最少一条从起点到终点的道路。
· 判断时,固然可以选择使用大量的if-else语句实现,由于 0|1 = 1,0|2 = 2,1|2 = 3(二进制),即可以使用 0 按位或墙的四个相邻节点,若结果为 3 即表示四个相邻节点中,最少存在一个 1 且最少存在一个 2。
另外,给起点的连通块染色时,若发现终点的颜色和起点的颜色相同,即表示起点与终点之间已经存在一条合法路径,即满足条件 1。
由于点的个数为 n·m,且每个点最多被染色 1 次,故时间复杂度为。
代码一(DFS染色)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3+10;
int n, m, A, B, C, D;
int color[N][N];
char g[N][N];
int dx[]={-1,0,1,0}, dy[]={0,1,0,-1};
void dfs(int x, int y, int c){
color[x][y] = c;
for(int i = 0; i < 4; i++){
int tx = x+dx[i], ty = y+dy[i];
if(tx<0||ty<0||tx>=n||ty>=m||g[tx][ty]=='#'||color[tx][ty])
continue;
dfs(tx, ty, c);
}
}
bool check(int x, int y){
//0|1=1 0|2=2 1|2=3(二进制)
int u = 0;
for(int i = 0; i < 4; i++){
int tx = x+dx[i], ty = y+dy[i];
if(tx<0||ty<0||tx>=n||ty>=m)
continue;
u |= color[tx][ty];
}
return u == 3;
}
int main(){
cin >> n >> m;
cin >> A >> B >> C >> D;
A--, B--, C--, D--;
for(int i = 0; i < n; i++)//无空格,做一行
cin >> g[i];
//起点染色
dfs(A, B, 1);
//判断是否通路
if(color[C][D]==1){
cout << "Yes";
return 0;
}
//终点染色
dfs(C, D, 2);
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
if(g[i][j]=='#')
if(check(i, j)){
cout << "Yes";
return 0;
}
cout << "No";
return 0;
}
代码二(BFS染色)
#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
const int N = 1e3+10;
typedef pair<int, int> PII;
int n, m, A, B, C, D;
char g[N][N];
int color[N][N];
int dx[]={-1,0,1,0}, dy[]={0,1,0,-1};
void bfs(int x, int y, int c){
queue<PII> q;
q.push({x, y});
color[x][y] = c;
while(!q.empty()){
auto t = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int tx = t.x + dx[i], ty = t.y + dy[i];
if(tx < 0||ty < 0|| tx >= n||ty >= m||g[tx][ty]=='#'||color[tx][ty])
continue;
color[tx][ty] = c;
q.push({tx, ty});
}
}
}
bool check(int x, int y){
int u = 0;
for(int i = 0; i < 4; i++){
int tx = x + dx[i], ty = y + dy[i];
if(tx < 0||ty < 0|| tx >= n||ty >= m)
continue;
u |= color[tx][ty];
}
return u==3;
}
int main(){
cin >> n >> m;
cin >> A >> B >> C >> D;
A--, B--, C--, D--;
for(int i = 0; i < n; i++)
cin >> g[i];
//染色
bfs(A, B, 1);
if(color[C][D]==1){
cout << "Yes";
return 0;
}
bfs(C, D, 2);
//check
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
if(g[i][j]=='#')
if(check(i, j)){
cout << "Yes";
return 0;
}
cout << "No";
return 0;
}
总结
代码大致流程为,起点染色、判断是否有合法通路(是则输出Yes)、若为否则终点染色、判断墙的四周是否大于等于两种颜色(是则输出Yes/否则No)。特别地,在墙壁四周判断时根据染色的色号1/2,在二进制中0|1=1/0|2=2/1|2=3,遍历4次最后的结果若为3,则表示四周有大于等于两种颜色,即砸开该墙壁后有一条合法通路,输出Yes。