把 01 矩阵中所有 1 作为 BFS 源点,多源同时扩展,每个格子第一次被访问到的层数就是到最近 1 的曼哈顿距离。
OJ: roj
题目 ID: 3067
难度:普及
标签:BFS搜索多源BFS
创建: 2026-10-01 13:42
更新: 2026-10-07 13:50
后置题目
roj 2050usaco-3.2.5 魔板A 教的分层 BFS「第一次到达的层数就是最小层数,首次入队即锁定」正是 B 状态图 BFS 的纪律二:`if nxt in parent: continue` 就是第一次到达就锁定,命中目标即回溯出最短操作串,B 只是把网格四邻换成三种置换、再额外叠加按 A/B/C 生成顺序保证字典序,所以 A 是 B 的 BFS 骨架前置。roj 2036usaco-2.4.2 穿越栅栏B 直接把 A 教的多源 BFS 源点初始化(源点距离置 0 一起入队)搬到迷宫外圈,只是把源点从 01 矩阵里的所有 1 换成补边后最外圈整片自由空间,再叠加字符位折半换算luogu P1144最短路计数B 直接复用 A 教的那一步「BFS 首次访问即最短距离、dist 兼作访问标记」(B 代码里 dist_node[v]==-1 时置 dist_node[u]+1 并 ways[v]=ways[u]),只是在这次分层求距离之上再叠加一条额外流程:对 dist[v]==dist[u]+1 的同层边累加 ways[v]+=ways[u] 做最短路计数。luogu P1032[NOIP 2002 提高组] 字串变换(疑似错题)B 的隐式图 BFS 直接沿用 A 教的「队列层数即最短距离、第一次到达即最近、入队即标记」这一判定:main.cpp 里 vis 判重后 step+1 入队、首次弹出目标串即返回步数,只是把 A 的网格格子换成字符串状态,并额外叠加每条规则全部出现位置的枚举、10 步截断与暴力对拍;A 的多源部分未被用到,故属 BFS 骨架的模板级复用。
题目描述
给定
思路
所有 1 同时作为源点开始多源 BFS 逐层向四邻扩散,某格子首次被扩散到的层数即为到最近 1 的距离;每格最多入队一次,
参考代码
Python 版:
python
import sys
from collections import deque
def main() -> None:
data = iter(sys.stdin.buffer.read().split())
n, m = int(next(data)), int(next(data))
# 矩阵压成一维,格子 i 的行列为 (i // m, i % m)
# 每行的数字之间没有空格,所以一个 token 就是一行,按行数顺序消费即可
g = b"".join(next(data) for _ in range(n)) # n 行拼接成一长串,共 n*m 个字符
dist = [-1] * (n * m)
q = deque()
for i in range(n * m):
if g[i] == 49: # 字符 '1':作为多源 BFS 的源点,距离 0
dist[i] = 0
q.append(i)
while q: # 多源 BFS:所有源点同层扩展,pop 时距离即最近 1 的曼哈顿距离
u = q.popleft()
r, c = divmod(u, m)
d = dist[u] + 1
for v in (u - m if r else -1, u + m if r < n - 1 else -1, u - 1 if c else -1, u + 1 if c < m - 1 else -1):
if v >= 0 and dist[v] < 0:
dist[v] = d
q.append(v)
# 按行拼好输出,避免逐格 join
print("\n".join(" ".join(map(str, dist[i : i + m])) for i in range(0, n * m, m)))
main()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 11:40
* update_at: 2026-10-06 11:40
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1005; // 行数上限
const int MAXM = 1005; // 列数上限
const int MAXV = MAXN * MAXM; // 一维总格子数上限
int n, m; // 矩阵行列
char g[MAXN][MAXM]; // 输入的 01 矩阵
int dist[MAXV]; // 一维下标到最近 1 的曼哈顿距离,-1 表示未访问
int q[MAXV]; // 手写队列
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%s", g[i]);
int total = n * m;
for (int i = 0; i < total; ++i) dist[i] = -1;
int head = 0, tail = 0;
// 把所有 1 作为多源 BFS 的源点,距离为 0,一起入队
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (g[i][j] == '1') {
int u = i * m + j;
dist[u] = 0;
q[tail++] = u;
}
}
}
// 多源 BFS:每轮扩展一层,第一次到达某格时的层数即最近距离
while (head < tail) {
int u = q[head++];
int r = u / m; // 行号
int c = u % m; // 列号
int d = dist[u] + 1; // 邻居距离
// 上
if (r > 0) {
int v = u - m;
if (dist[v] < 0) {
dist[v] = d;
q[tail++] = v;
}
}
// 下
if (r + 1 < n) {
int v = u + m;
if (dist[v] < 0) {
dist[v] = d;
q[tail++] = v;
}
}
// 左
if (c > 0) {
int v = u - 1;
if (dist[v] < 0) {
dist[v] = d;
q[tail++] = v;
}
}
// 右
if (c + 1 < m) {
int v = u + 1;
if (dist[v] < 0) {
dist[v] = d;
q[tail++] = v;
}
}
}
// 按原矩阵形状输出距离
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (j) putchar(' ');
printf("%d", dist[i * m + j]);
}
putchar('\n');
}
return 0;
}