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 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 — number of test cases.
The first line of each test case contains two integers and (; ) — the number of rows and columns, respectively.
Then rows follow, -th row contains a string of length , where is '', if a cactus grows at the intersection of the -th row and the -th column. Otherwise, is ''.
The sum of over all test cases does not exceed .
Output
For each test case, print in the first line if it is impossible to build a cactus wall without breaking the rules. Otherwise, print in the first line, then print lines of characters each — the field itself, where the -th character of the -th line is equal to '', if there is a cactus on the intersection of the -th row and the -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
#..
.#.
#.#
...
解题思路
这题关键是要发现如果存在一条从第列斜着走到第列的仙人掌路径,那么就一定不存在从第行走到第行的路径。
比如样例中的:
又或者:
因此问题就变成了是否能够构造出一条从第列斜着走到第列的仙人掌路径,如果不存在则无解,如果存在则给出添加仙人掌数量最少的一条路径。
由于是斜着走(仙人掌路径),因此一个格子的四个方向是左上、右上、右下、左下。求添加仙人掌数量最小的路径本质就是求最短路,可以用双端队列宽搜,如果走到的下一个格子是仙人掌,那么权值为;否则是空地,权值为,需要注意的是由于空地需要放仙人掌,因此需要保证这个空地的上下左右四个方向都没有仙人掌。
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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16894950.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效