BFS(广度优先搜索) 是解决最短路径、层级扩散、网格遍历 问题的核心算法,而多源 BFS 更是处理多点同时扩散场景的最优解 ------ 比如病毒扩散、火势蔓延、多起点最短距离等经典问题。
一、单源 BFS
1. 什么是单源 BFS?
单源 BFS = 从一个起点出发 ,按照「层级」向外扩散,第一次访问到某个节点时,就是从起点到该节点的最短距离 / 最少步数。
2. 核心特性
- 按层遍历:离起点越近,越先被访问;
- 最短路径保证:无权图 / 网格中,BFS 天然求最短距离;
- 队列实现:用队列存储待遍历节点,先进先出。
3. 适用场景
- 迷宫找最短路径
- 单点到其他点的最小步数
- 层级遍历类问题
二、多源 BFS
1. 什么是多源 BFS?
多源 BFS = 从多个起点同时出发 ,所有起点一起按层扩散,第一次访问到某个节点时,就是该节点到「最近起点」的最短距离。
这是它最强大的地方:一次遍历,求出所有点到最近源点的最短距离。
2. 单源 BFS VS 多源 BFS(直观对比)
| 类型 | 起点数量 | 遍历方式 | 求解目标 |
|---|---|---|---|
| 单源 BFS | 1 个 | 逐个起点扩散 | 单点到其他点的最短距离 |
| 多源 BFS | 多个 | 所有起点同时扩散 | 任意点到最近起点的最短距离 |
3. 多源 BFS 核心原理
初始化时,把所有源点一次性全部加入队列,其余逻辑和普通 BFS 完全一致。
因为队列会按「层」处理,所有源点同属第 0 层,保证了扩散的同步性。
三、多源 BFS 标准实现模板
以网格扩散为例,给出可套用的模板。
1. 通用变量定义
cpp
#include <bits/stdc++.h>
using namespace std;
// 网格最大范围(根据题目调整)
const int MAX = 505;
// 距离数组:-1 = 未访问/未扩散
int dis[MAX][MAX];
// 方向数组:上下左右
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int n, m; // 网格行数、列数
2. 多源 BFS 核心函数
cpp
// 传入存储所有起点的队列
void bfs(queue<pair<int, int>> &q) {
while (!q.empty()) {
// 取出当前节点
auto [x, y] = q.front();
q.pop();
// 向四个方向扩散
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
// 边界判断 + 未访问判断
if (nx < 1 || ny < 1 || nx > n || ny > m || dis[nx][ny] != -1)
continue;
// 更新距离:上一层 + 1
dis[nx][ny] = dis[x][y] + 1;
// 新节点入队
q.push({nx, ny});
}
}
}
3. 初始化
cpp
// 1. 初始化距离数组为 -1
memset(dis, -1, sizeof(dis));
queue<pair<int, int>> q;
// 2. 多源核心:一次性加入所有起点!
// 假设这里有若干个起点 (x1,y1),(x2,y2)...
for (所有起点) {
dis[x][y] = 0; // 源点距离为0
q.push({x, y}); // 全部入队
}
// 3. 执行BFS
bfs(q);
4. 模板总结
多源 BFS 和普通 BFS 代码几乎一样 ,唯一区别: 初始化队列时,把所有源点一起塞进去,而不是只塞一个。
四、多源 BFS 为什么正确?
我们用一个简单例子理解:
- 源点 A:(1,1)
- 源点 B:(5,4)
执行步骤:
- 队列初始:
[(1,1), (5,4)],距离都是 0; - 第一层扩散:两个源点同时向外走一步,距离 = 1;
- 第二层扩散:所有距离 = 1 的点同时向外走一步;
任何一个点,只会被「最近的源点」最先扩散到,并标记最短距离,后续不会再被更新。
这就是多源 BFS 能保证「最短距离」的底层逻辑。
五、多源 BFS 高频适用场景
多源 BFS 是算法竞赛必背模板,适用所有「多点同时扩散」问题:
- 网格扩散类
- 病毒 / 瘟疫扩散(血色先锋队)
- 火势蔓延、洪水扩散
- 距离求解类
- 每个 0 到最近 1 的距离(LeetCode 542)
- 多起点到任意点的最短路径
- 图论扩展
- 无向图中多个源点的最短路问题
六、结合例题:血色先锋队
我们用这道题验证多源 BFS 模板:
- 题目:多个感染源同时扩散瘟疫,求领主被感染的时间(最短距离);
- 感染源 = 多源 BFS 起点;
- 感染时间 = 距离数组值。
完整代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX = 505;
int dis[MAX][MAX];
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int n, m, a, b;
void bfs(queue<pair<int, int>> &q) {
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 1 || ny < 1 || nx > n || ny > m || dis[nx][ny] != -1)
continue;
dis[nx][ny] = dis[x][y] + 1;
q.push({nx, ny});
}
}
}
int main() {
// 快速读写:应对大数据输入
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> a >> b;
memset(dis, -1, sizeof(dis));
queue<pair<int, int>> q;
// 步骤1:所有感染源入队(多源核心)
for (int i = 0; i < a; i++) {
int x, y;
cin >> x >> y;
dis[x][y] = 0;
q.push({x, y});
}
// 步骤2:存储领主位置
vector<pair<int, int>> lords;
for (int i = 0; i < b; i++) {
int x, y;
cin >> x >> y;
lords.emplace_back(x, y);
}
// 步骤3:执行多源BFS
bfs(q);
// 步骤4:输出答案
for (auto [x, y] : lords)
cout << dis[x][y] << '\n';
return 0;
}
代码亮点
- 严格遵循多源 BFS 模板,无多余逻辑;
- 加入快速读写,适配大数据量;
- 代码简洁通用,可直接迁移到同类题目。
七、核心要点
- 多源 BFS = 同时启动的普通 BFS;
- 唯一区别:初始化队列时加入所有起点;
- 核心保证:第一次访问 = 到最近源点的最短距离;
- 万能模板:网格题直接套用,修改边界和输入即可。
八、总结
- 单源 BFS 解决单点扩散 ,多源 BFS 解决多点同时扩散;
- 多源 BFS 代码极简,只改初始化,不改遍历逻辑;
- 它是解决网格最短距离、扩散类问题的最优算法,时间复杂度 O (n×m);