题解:洛谷 AT_abc460_d Repeatedly Repainting
【题目来源】
洛谷:AT_abc460_d [ABC460D] Repeatedly Repainting - 洛谷
【题目描述】
There is a grid with \(H\) rows and \(W\) columns. The cell at the \(i\)-th row from the top and the \(j\)-th column from the left is called cell \((i, j)\).
Every cell is colored white or black. The grid is described by \(H\) strings \(S_1, S_2, \ldots, S_H\), each of length \(W\). If the \(j\)-th character of \(S_i\) is ., cell \((i, j)\) is white; if the \(j\)-th character of \(S_i\) is #, cell \((i, j)\) is black.
You perform the following operation \(10^{100}\) times.
- Simultaneously apply the following rules to all cells.
- A cell that is white before the operation becomes black if and only if there exists at least one black cell adjacent to it. Here, cells \((x, y)\) and \((x', y')\) are adjacent if and only if one of them is within the \(8\)-neighborhood of the other, that is, \(\max(|x-x'|, |y-y'|) = 1\).
- A cell that is black before the operation becomes white.
Find the color of each cell after the operations.
有一个 \(H\) 行 \(W\) 列的网格。从上往下第 \(i\) 行、从左往右第 \(j\) 列的单元格称为单元格 \((i, j)\)。
每个单元格被涂成白色或黑色。网格由 \(H\) 个字符串 \(S_1, S_2, \ldots, S_H\) 描述,每个字符串长度为 \(W\)。如果 \(S_i\) 的第 \(j\) 个字符是 .,则单元格 \((i, j)\) 是白色的;如果是 #,则单元格 \((i, j)\) 是黑色的。
你将执行以下操作 \(10^{100}\) 次。
- 同时对所有单元格应用以下规则。
- 在操作前为白色的单元格变为黑色,当且仅当存在至少一个与之相邻的黑色单元格。这里,单元格 \((x, y)\) 和 \((x', y')\) 相邻当且仅当其中一个在另一个的 \(8\) 邻域内,即 \(\max(|x-x'|, |y-y'|) = 1\)。
- 在操作前为黑色的单元格变为白色。
求操作后每个单元格的颜色。
【输入】
The input is given from Standard Input in the following format:
\(H\) \(W\)
\(S_1\)
\(S_2\)
\(\vdots\)
\(S_H\)
【输出】
Output \(H\) lines.
Output one string of length \(W\) consisting of . and # per line.
The \(j\)-th character of the \(i\)-th line should be . if cell \((i, j)\) is white after \(10^{100}\) operations, and # if it is black.
【输入样例】
3 4
#.#.
.#..
#...
【输出样例】
#.#.
.#..
#..#
【核心思想】
-
问题分析:给定 \(H \times W\) 网格,每个格子初始为黑色(
#)或白色(.),执行 \(10^{100}\) 次操作:黑色变白,白色变黑的条件是八邻域有黑色。求最终颜色。这是一个多源BFS + 八方向扩展问题,关键在于发现操作规律与到最近黑格距离的奇偶性相关。 -
算法选择:
- 预处理:移除内部黑格:八邻域都是黑格的格子永远不会变白,先将其移除避免影响距离计算
- 多源BFS:将所有剩余黑格同时入队作为起点,计算每个位置到最近黑格的距离
- 八方向扩展:支持上下左右和四个对角线方向的移动
- 奇偶性判断:距离为偶数最终为黑,距离为奇数最终为白
-
关键步骤:
- 初始化:读取 \(H\)(行数)、\(W\)(列数)和网格 \(S\)
- 识别并移除内部黑格:
- 遍历所有黑格,检查其八邻域是否都是黑格
- 如果是,标记为内部黑格并移除(变为白格)
- 多源BFS初始化:将所有非内部黑格入队,距离设为 \(0\)
- 八方向BFS:向八个方向扩展,更新距离数组 \(f\)
- 根据奇偶性输出:\(f[i][j] \% 2 == 0\) 输出
#,否则输出.
-
时间/空间复杂度:
- 时间复杂度:\(O(H \times W)\),每个格子最多访问一次
- 空间复杂度:\(O(H \times W)\),需要存储网格和距离数组
-
多源BFS + 八方向扩展的核心思想:
- 多源BFS:多个起点同时开始搜索,天然计算每个位置到最近起点的距离
- 八邻域:考虑上下左右和四个对角线方向,共8个方向
- 距离与奇偶性:操作次数极大时,最终颜色只与到最近黑格距离的奇偶性有关
- 内部障碍处理:完全被包围的黑格不影响其他格子的距离计算,需要先移除
- 适用于计算到多个目标点的最短距离、网格扩散类问题
【算法标签】
普及+ #BFS-二维
【代码详解】
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1000005;
int h, w; // 网格的行数和列数
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; // 八个方向的x偏移
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; // 八个方向的y偏移
vector<string> s; // 存储网格
vector<vector<int> > f; // 存储每个位置的距离
void bfs() // 广度优先搜索计算距离
{
queue<PII> q;
for (int i = 1; i <= h; i++) // 初始化队列
for (int j = 1; j <= w; j++)
{
if (s[i][j] == '#') // 如果当前位置是障碍
{
q.push({i, j}); // 入队
f[i][j] = 0; // 距离为0
}
}
while (!q.empty()) // 当队列不为空
{
int x = q.front().first, y = q.front().second;
q.pop(); // 出队
for (int i = 0; i < 8; i++) // 遍历八个方向
{
int nx = x + dx[i], ny = y + dy[i];
if (nx < 1 || nx > h || ny < 1 || ny > w || f[nx][ny] != -1) // 越界或已访问
continue;
f[nx][ny] = f[x][y] + 1; // 更新距离
q.push({nx, ny}); // 入队
}
}
}
int main()
{
cin >> h >> w; // 输入行数和列数
s.resize(h + 1);
f.assign(h + 1, vector<int>(w + 1, -1)); // 初始化距离数组为-1
for (int i = 1; i <= h; i++) // 输入网格
{
cin >> s[i];
s[i] = " " + s[i]; // 在字符串前加空格,使下标从1开始
}
queue<PII> q;
for (int i = 1; i <= h; i++) // 寻找内部障碍
for (int j = 1; j <= w; j++)
{
if (s[i][j] == '#') // 如果当前位置是障碍
{
bool flag = 1;
for (int k = 0; k < 8; k++) // 检查八个方向
{
int nx = i + dx[k], ny = j + dy[k];
if (nx < 1 || nx > h || ny < 1 || ny > w) // 越界
continue;
if (s[nx][ny] == '.') // 如果邻居是空地
{
flag = 0; // 不是内部障碍
break;
}
}
if (flag) // 如果是内部障碍
q.push({i, j});
}
}
while (!q.empty()) // 移除内部障碍
{
int x = q.front().first, y = q.front().second;
q.pop();
s[x][y] = '.'; // 将内部障碍变为空地
}
bfs(); // 计算距离
for (int i = 1; i <= h; i++) // 输出结果
{
for (int j = 1; j <= w; j++)
{
if (f[i][j] % 2 == 0) // 如果距离是偶数
cout << "#";
else
cout << ".";
}
cout << endl;
}
return 0;
}
【运行结果】
3 4
#.#.
.#..
#...
#.#.
.#..
#..#
浙公网安备 33010602011771号