多源BFS

多源BFS

一、简介

多源BFS与单源BFS有什么区别呢?

  • 单源BFS:从某一个点开始(起点)
  • 多源BFS:从多个点同时开始走

如何解决多源BFS?

多源BFS:多个起点 ——> 多个起点同时加入队列!

核心:在求解多源BFS问题时,同时将所有起点加入队列即可!

二、练习

1.矩阵距离

【题目链接】173. 矩阵距离 - AcWing题库

给定一个 NN 行 MM 列的 0101 矩阵 AA,A[i] [j] 与A[k] [l] 之间的曼哈顿距离定义为:

dist(A[i] [j],A[k] [l])=|i−k|+|j−l|

输出一个 N 行 M 列的整数矩阵 B,其中:

B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])

输入格式

第一行两个整数 N,M。

接下来一个 N 行 M 列的 01 矩阵,数字之间没有空格。

输出格式

一个 N 行 M 列的矩阵 B,相邻两个整数之间用一个空格隔开。

数据范围

1≤N,M≤1000

输入样例:

3 4
0001
0011
0110

输出样例:

3 2 1 0
2 1 0 0
1 0 0 1

思路:

  1. 将所有的1的点(起点)加入队列
  2. 按照普通宽搜的方式,将其他未被搜过的点扩展出来,通过d[N] [N]数组标志且维护距离。这样就得到了每个点到所有1的最短距离!

image

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
queue<PII> q;

const int N = 1010;
char g[N][N];
int d[N][N];//存放非1的点到所有1的点的最短距离

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;

void bfs()
{
    memset(d, -1, sizeof d);
    
    //将所有起点加入队列
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
            {
                if(g[i][j] == '1')
                {
                    q.push({i, j});
                    d[i][j] = 0;//起点到自己的距离为0
                }
            }
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        
        for(int i = 0; i < 4; i ++)
        {
            int a = t.first + dx[i], b = t.second + dy[i];
            if(a < 0 || a >= n || b < 0 || b >= m) continue;
            if(d[a][b] != -1) continue;
            
            q.push({a, b});//邻接节点入队
            d[a][b] = d[t.first][t.second] + 1;//更新距离
        }
    }
            
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);
    
    bfs();
    
    //输出距离矩阵
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ ) printf("%d ", d[i][j]);
        puts("");
    }
    
    return 0;
}

2.扩散

【题目描述】

小蓝在一张无限大的特殊画布上作画。

这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。

小蓝在画布上首先点了一下几个点:(0,0),(2020,11),(11,14),(2000,2000)。只有这几个格子上有黑色,其它位置都是白色的。

每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色(如果原来就是黑色,则还是黑色)。

请问,经过 2020 分钟后,画布上有多少个格子是黑色的。

本题无输入,请直接输出一个答案即可。

思路:

每分钟扩散1点,说明了边权都为1,而且有多个起点,因此可以使用多源BFS,维护一个d[i][j]数组来记录当前的点是第几分钟的。

多源BFS:将所有黑墨水加入队列,然后宽搜染色或者统计可以被染色的白点的个数。若采用染黑的方法最后再遍历地图统计即可;若统计的是可以被染成黑色的白点个数,最后答案要加4(原来的4个黑点)。

注:

  • 由于没有地图给我们。我们先构建一张尽可能大的地图

  • 位置(0,0)若是向上下左右四个方向扩散的话就会出现负坐标的情况,如(0, -2020),但是数组是没有负数下标的,因此我们可以扩大初始坐标,如将原来的4个初始起点加上3000

【代码实现】

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
queue<PII> q;

const int maxn = 10000;
char g[maxn][maxn];
int d[maxn][maxn];//记录距离

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int cnt;

void bfs()
{
    memset(d, -1, sizeof d);
    
    //所有起点加入队列
    q.push({3000, 3000});
    q.push({5020, 3011});
    q.push({3011, 3014});
    q.push({5000, 5000});
    //初始化距离
    d[3000][3000] = d[5020][3011] = d[3011][3014] = d[5000][5000] = 0;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        if(d[t.first][t.second] == 2020) break ;// 2020分钟后结束 
        
        for(int i = 0; i < 4; i ++)
        {
            int a = t.first + dx[i], b = t.second + dy[i];
            if(a < 0 || a >= maxn || b < 0 || b >= maxn) continue;
            if(d[a][b] != -1 || g[a][b] == '#') continue;
            
            //可以被染黑的节点入队 
            q.push({a, b});
            d[a][b] = d[t.first][t.second] + 1;
			cnt ++;
        }
    }
}

int main()
{
    
    //自己建图
    for (int i = 0; i < maxn; i ++ )
    {
        for(int j = 0; j < maxn; j ++)
            g[i][j] = '.';
    }
    //四个起点标记为黑墨水
    g[3000][3000] = g[5020][3011] = g[3011][3014] = g[5000][5000] = '#';
    
    bfs();//统计可以被4个起点染黑的白点个数 
    
    cout << cnt + 4;//总的黑点个数:被染黑 加上 原来的4个黑点 
    
    return 0;
}

答案:20312088

三、总结

多源BFS的核心就是:将多个起点都加入队列,然后再宽搜!

学习内容参考:
蓝桥杯2020国赛真题
acwing算法提高课

注:如果文章有任何错误或不足,请各位大佬尽情指出,评论留言留下您宝贵的建议!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦


欢迎访问:本人博客园地址

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值