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×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 (1t103) — number of test cases.

The first line of each test case contains two integers n and m (2n,m2105; n×m4105) — the number of rows and columns, respectively.

Then n rows follow, i-th row contains a string si of length m, where si,j is '#', if a cactus grows at the intersection of the i-th row and the j-th column. Otherwise, si,j is '.'.

The sum of n×m over all test cases does not exceed 4105.

Output

For each test case, print NO in the first line if it is impossible to build a cactus wall without breaking the rules. Otherwise, print 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 '#', if there is a cactus on the intersection of the i-th row and the j-th column, otherwise it is '.'. 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 @   onlyblues  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示