多源 BFS

BFS(广度优先搜索) 是解决最短路径、层级扩散、网格遍历 问题的核心算法,而多源 BFS 更是处理多点同时扩散场景的最优解 ------ 比如病毒扩散、火势蔓延、多起点最短距离等经典问题。


一、单源 BFS

1. 什么是单源 BFS?

单源 BFS = 从一个起点出发 ,按照「层级」向外扩散,第一次访问到某个节点时,就是从起点到该节点的最短距离 / 最少步数

2. 核心特性

  1. 按层遍历:离起点越近,越先被访问;
  2. 最短路径保证:无权图 / 网格中,BFS 天然求最短距离;
  3. 队列实现:用队列存储待遍历节点,先进先出。

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,1), (5,4)],距离都是 0;
  2. 第一层扩散:两个源点同时向外走一步,距离 = 1;
  3. 第二层扩散:所有距离 = 1 的点同时向外走一步;

任何一个点,只会被「最近的源点」最先扩散到,并标记最短距离,后续不会再被更新。

这就是多源 BFS 能保证「最短距离」的底层逻辑。


五、多源 BFS 高频适用场景

多源 BFS 是算法竞赛必背模板,适用所有「多点同时扩散」问题:

  1. 网格扩散类
    • 病毒 / 瘟疫扩散(血色先锋队)
    • 火势蔓延、洪水扩散
  2. 距离求解类
    • 每个 0 到最近 1 的距离(LeetCode 542)
    • 多起点到任意点的最短路径
  3. 图论扩展
    • 无向图中多个源点的最短路问题

六、结合例题:血色先锋队

我们用这道题验证多源 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;
}

代码亮点

  1. 严格遵循多源 BFS 模板,无多余逻辑;
  2. 加入快速读写,适配大数据量;
  3. 代码简洁通用,可直接迁移到同类题目。

七、核心要点

  1. 多源 BFS = 同时启动的普通 BFS
  2. 唯一区别:初始化队列时加入所有起点;
  3. 核心保证:第一次访问 = 到最近源点的最短距离;
  4. 万能模板:网格题直接套用,修改边界和输入即可。

八、总结

  1. 单源 BFS 解决单点扩散 ,多源 BFS 解决多点同时扩散
  2. 多源 BFS 代码极简,只改初始化,不改遍历逻辑
  3. 它是解决网格最短距离、扩散类问题的最优算法,时间复杂度 O (n×m);
相关推荐
程序员卷卷狗2 小时前
Java转Go面试速记:Go基础22问,一篇理清高频易错点一篇理清高频易错点
java·面试·golang
zzzzz3692 小时前
快速搭建SpringAi项目 集成智能问答,RAG,FUINCTION_CALLING等功能
java·ai编程
smith成长之旅2 小时前
07 | Mem0 框架分析:三路信号融合——语义 + BM25 + Entity Boost 的混合检索
python·算法
wabs6662 小时前
关于贪心算法章节的【有两个维度问题】的自我总结
算法·贪心算法
笨蛋不要掉眼泪2 小时前
Java并发编程 :深入剖析LinkedBlockingQueue
java·开发语言·网络·并发
不会C语言的男孩2 小时前
C++ Primer Plus 第10章:对象和类
开发语言·c++
不会C语言的男孩2 小时前
C++ Primer Plus 第11章:使用类
开发语言·c++
未若君雅裁2 小时前
算法复杂度与数据结构:Java 集合篇的第一块基石
java·数据结构·算法
致Great2 小时前
Claude Code 上线 Dynamic Workflows:一句话调度 1000 个子智能体并行干活
java·linux·服务器