E. Cactus Wall

E. Cactus Wall

Monocarp is playing Minecraft and wants to build a wall of cacti. He wants to build it on a field of sand of the size of $n \times m$ cells. Initially, there are cacti in some cells of the field. Note that, in Minecraft, cacti cannot grow on cells adjacent to each other by side — and the initial field meets this restriction. Monocarp can plant new cacti (they must also fulfil the aforementioned condition). He can't chop down any of the cacti that are already growing on the field — he doesn't have an axe, and the cacti are too prickly for his hands.

Monocarp believes that the wall is complete if there is no path from the top row of the field to the bottom row, such that:

  • each two consecutive cells in the path are adjacent by side;
  • no cell belonging to the path contains a cactus.

Your task is to plant the minimum number of cacti to build a wall (or to report that this is impossible).

Input

The first line contains a single integer $t$ $(1 \leq t \leq {10}^{3})$ — number of test cases.

The first line of each test case contains two integers $n$ and $m$ ($2 \leq n,m \leq 2 \cdot {10}^{5}$; $n \times m \leq 4 \cdot {10}^{5}$) — the number of rows and columns, respectively.

Then $n$ rows follow, $i$-th row contains a string $s_i$ of length $m$, where $s_{i,j}$ is '$\text{#}$', if a cactus grows at the intersection of the $i$-th row and the $j$-th column. Otherwise, $s_{i,j}$ is '$\text{.}$'.

The sum of $n \times m$ over all test cases does not exceed $4 \cdot {10}^{5}$.

Output

For each test case, print $\text{NO}$ in the first line if it is impossible to build a cactus wall without breaking the rules. Otherwise, print $\text{YES}$ in the first line, then print $n$ lines of $m$ characters each — the field itself, where the $j$-th character of the $i$-th line is equal to '$\text{#}$', if there is a cactus on the intersection of the $i$-th row and the $j$-th column, otherwise it is '$\text{.}$'. If there are multiple optimal answers, print any of them.

Example

input

4
2 4
.#..
..#.
3 3
#.#
...
.#.
5 5
.....
.....
.....
.....
.....
4 3
#..
.#.
#.#
...

output

YES
.#.#
#.#.
NO
YES
....#
...#.
..#..
.#...
#....
YES
#..
.#.
#.#
...

 

解题思路

  这题关键是要发现如果存在一条从第$1$列斜着走到第$m$列的仙人掌路径,那么就一定不存在从第$1$行走到第$n$行的路径。

  比如样例中的:

  又或者:

  因此问题就变成了是否能够构造出一条从第$1$列斜着走到第$m$列的仙人掌路径,如果不存在则无解,如果存在则给出添加仙人掌数量最少的一条路径。

  由于是斜着走(仙人掌路径),因此一个格子的四个方向是左上、右上、右下、左下。求添加仙人掌数量最小的路径本质就是求最短路,可以用双端队列宽搜,如果走到的下一个格子是仙人掌,那么权值为$0$;否则是空地,权值为$1$,需要注意的是由于空地需要放仙人掌,因此需要保证这个空地的上下左右四个方向都没有仙人掌。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef pair<int, int> PII;
 5 
 6 int n, m;
 7 vector<string> g;
 8 vector<vector<int>> dist;
 9 vector<vector<bool>> vis;
10 vector<vector<PII>> path;
11 
12 bool check(int sx, int sy) {    // 检查空地上下左右四个方向是否存在仙人掌
13     int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
14     for (int i = 0; i < 4; i++) {
15         int x = sx + dx[i], y = sy + dy[i];
16         if (x < 0 || x >= n || y < 0 || y >= m) continue;
17         if (g[x][y] == '#') return false;   // 某个方向存在仙人掌,那么(sx, sy)不可以放仙人掌
18     }
19     return true;
20 }
21 
22 void bfs() {
23     deque<PII> q;
24     dist = vector<vector<int>>(n, vector<int>(m, 0x3f3f3f3f));
25     vis = vector<vector<bool>>(n, vector<bool>(m, 0));
26     path = vector<vector<PII>>(n, vector<PII>(m, {-1, -1}));
27     
28     for (int i = 0; i < n; i++) {   // 第一列的位置入队
29         if (g[i][0] == '#') q.push_front({i, 0}), dist[i][0] = 0;   // 如果是仙人掌则直接入队,权值为0
30         else if (check(i, 0)) q.push_back({i, 0}), dist[i][0] = 1;  // 如果是空地需要保证四周没有仙人掌,权值为1
31     }
32     
33     int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1}; // 斜着的四个方向
34     
35     while (!q.empty()) {
36         PII t = q.front();
37         q.pop_front();
38         if (vis[t.first][t.second]) continue;
39         vis[t.first][t.second] = true;
40         
41         for (int i = 0; i < 4; i++) {
42             int x = t.first + dx[i], y = t.second + dy[i];
43             if (x < 0 || x >= n || y < 0 || y >= m) continue;
44             if (g[x][y] == '#' && dist[x][y] > dist[t.first][t.second]) {   // 下一个格子是仙人掌
45                 dist[x][y] = dist[t.first][t.second];
46                 path[x][y] = {t.first, t.second};
47                 q.push_front({x, y});
48             }
49             else if (g[x][y] == '.' && check(x, y) && dist[x][y] > dist[t.first][t.second] + 1) {   // 下一个格子是空地,一样要保证(x, y)四周没有仙人掌
50                 dist[x][y] = dist[t.first][t.second] + 1;
51                 path[x][y] = {t.first, t.second};
52                 q.push_back({x, y});
53             }
54         }
55     }
56 }
57 
58 void solve() {
59     cin >> n >> m;
60     g = vector<string>(n);
61     for (int i = 0; i < n; i++) {
62         cin >> g[i];
63     }
64     
65     bfs();
66     
67     int x = -1, y = -1; // 找到最短路径最后一列的坐标位置
68     for (int i = 0, minv = 0x3f3f3f3f; i < n; i++) {
69         if (minv > dist[i][m - 1]) {
70             minv = dist[i][m - 1];
71             x = i, y = m - 1;
72         }
73     }
74     
75     if (x == -1) {
76         cout << "NO\n";
77     }
78     else {
79         cout << "YES\n";
80         while (x != -1) {
81             g[x][y] = '#';  // 整条最短路径都标为#
82             PII t = path[x][y];
83             x = t.first, y = t.second;
84         }
85         for (int i = 0; i < n; i++) {
86             cout << g[i] << '\n';
87         }
88     }
89 }
90 
91 int main() {
92     int t;
93     scanf("%d", &t);
94     while (t--) {
95         solve();
96     }
97     
98     return 0;
99 }

 

参考资料

  Educational Codeforces Round 138 D(数学) E(最短路):https://zhuanlan.zhihu.com/p/575721113

posted @ 2022-11-16 10:10  onlyblues  阅读(56)  评论(0编辑  收藏  举报
Web Analytics