DFS/BFS染色算法之混镜之地2(蓝桥)

问题描述

小蓝有一天误入了一个混境之地。

好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:

  1. 混境之地的大小为 n⋅m,其中 # 表示不可通过的墙壁,. 表示可以走的路。
  2. 他现在所在位置的坐标为 (A,B) ,而这个混境之地出口的坐标为 (C,D) ,当站在出口时即表示可以逃离混境之地。
  3. 有一句神奇的咒语可以在混境之地中使用,可以击破一面墙壁,即将 # 变为 . 。

坏消息是:神奇的咒语副作用很大,会导致使用者身心俱疲,所以最多只能使用一次。

小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,则输入 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++2s512M
C2s512M
Java3s512M
Python34s512M

 解题思路(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 次,故时间复杂度为O(nm)

代码一(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。

  • 39
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值