多源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的点(起点)加入队列
- 按照普通宽搜的方式,将其他未被搜过的点扩展出来,通过d[N] [N]数组标志且维护距离。这样就得到了每个点到所有1的最短距离!
【代码实现】
#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算法提高课
注:如果文章有任何错误或不足,请各位大佬尽情指出,评论留言留下您宝贵的建议!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦
欢迎访问:本人博客园地址