1.模板
BFS是广度优先搜索(Breadth-First Search)的缩写,是一种常用的图遍历算法。它从起点开始,先遍历所有与起点相邻的节点,再逐层向外遍历,直到遍历完整个图。
BFS算法可以用于许多问题,如查找图中两个节点之间的最短路径、生成所有可能的组合、求解迷宫等等。在实际应用中,BFS算法通常需要使用队列来实现。
使用BFS算法通常需要以下步骤:
- 定义输入和输出格式,例如图的邻接表表示、起点和终点等。
- 定义需要使用的变量,例如已访问节点集合、当前路径等。
- 实现BFS函数,在函数中进行BFS遍历,并根据具体问题进行相应的操作。BFS函数通常需要传入图、起点、终点等参数。
- 实现主函数,在主函数中调用BFS函数,并返回搜索结果。
1.典型的典型
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int g[N][N];//存储地图
int f[N][N];//存储距离
int n, m;
void bfs(int a, int b)//广度优先遍历
{
queue<PII> q;
q.push({a, b});
//初始点的距离为 0.
//可以不要这一句,因为f初始化的时候,各个点为0
f[0][0] = 0;
while(!q.empty())
{
PII start = q.front();
q.pop();
//这一句可以不要,因为入队的时候就置为了1
g[start.first][start.second] = 1;
int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
for(int i = 0; i < 4; i++)//往四个方向走
{
//当前点能走到的点
int x = start.first + dx[i], y = start.second + dy[i];
//如果还没有走过
if(g[x][y] == 0)
{
//走到这个点,并计算距离
g[x][y] = 1;
f[x][y] = f[start.first][start.second] + 1;//从当前点走过去,则距离等于当前点的距离+1.
//这个点放入队列,用来走到和它相邻的点。
q.push({x, y});
}
}
}
cout << f[n][m];
}
int main()
{
memset(g, 1, sizeof(g));
cin >> n >>m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> g[i][j];
}
}
bfs(1,1);
}
2.Flood Fill
Flood Fill是一种图像处理算法,用于填充连通区域。它从指定的起点开始,将所有与起点连通的区域填充为指定的颜色。
Flood Fill算法可以用于许多问题,如图像处理、游戏开发等。它可以用于填充图像中的连通区域、生成地形、着色等等。
使用Flood Fill算法通常需要以下步骤:
- 定义输入和输出格式,例如图像矩阵、起点和目标颜色等。
- 定义需要使用的变量,例如已访问节点集合、当前路径等。
- 实现Flood Fill函数,在函数中进行Flood Fill操作,并根据具体问题进行相应的操作。Flood Fill函数通常需要传入图像矩阵、起点、目标颜色、填充颜色等参数。
- 实现主函数,在主函数中调用Flood Fill函数,并返回处理结果。
1.求连通块数量
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1009;
int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1};//方向
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};//方向
int n, m, ans = 0;
char mp[N][N];
void dfs(int x, int y)
{
mp[x][y] = '.';//更新当前节点的类型
for(int i = 0;i < 8;++ i)//枚举八连通方向
if(mp[x + dx[i]][y + dy[i]] == 'W')//如果是水
dfs(x + dx[i],y + dy[i]);//dfs 下一个节点
}
int main()
{
cin >> n >> m;//输入
for(int i = 1;i <= n;++ i)
for(int j = 1;j <= m;++ j)
cin >> mp[i][j];//输入
for(int i = 1;i <= n;++ i)
for(int j = 1;j <= m;++ j)
if(mp[i][j] == 'W')
dfs(i, j), ans ++;//每递归一个连通块,ans ++
cout << ans << endl;
return 0;
}
2.求最大连通块面积
代码
#include <iostream>
#include <queue>
using namespace std;
typedef pair <int,int> PII;
const int N = 60;
int dx[] = {0,-1,0,1},dy[] = {-1,0,1,0};
int n,m,ans = 0,res = 0;
int g[N][N];
bool vis[N][N];
int bfs (int i,int j) {
queue <PII> q;
q.push ({i,j});
vis[i][j] = true;
int cnt = 0;
while (!q.empty ()) {
PII t = q.front ();
q.pop ();
cnt++;
for (int k = 0;k < 4;k++) {
if (g[t.first][t.second] >> k & 1) continue;//遍历可走的方向
int x = t.first + dx[k],y = t.second + dy[k];
if (vis[x][y]) continue;
q.push ({x,y});
vis[x][y] = true;
}
}
return cnt;
}
int main () {
cin >> n >> m;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) cin >> g[i][j];
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
if (!vis[i][j]) {//遍历每一个房间
ans++;
res = max (res,bfs (i,j));
}
}
}
cout << ans << endl << res << endl;
return 0;
}
3.有连通块相互限制的数量
代码
#include <iostream>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair <int,int> PII;
const int N = 1010;
int n;
int h[N][N];
bool st[N][N];
void bfs (int sx,int sy,bool &has_higher,bool &has_lower) {
queue <PII> q;
q.push ({sx,sy});
while (!q.empty ()) {
PII t = q.front ();
q.pop ();
for (int i = t.x - 1;i <= t.x + 1;i++) {
for (int j = t.y - 1;j <= t.y + 1;j++) {
if (i < 1 || i > n || j < 1 || j > n) continue;
if (h[i][j] != h[t.x][t.y]) {
if (h[i][j] > h[t.x][t.y]) has_higher = true;//判断此连通块是否合法
else has_lower = true;
}
else if (!st[i][j]) {
q.push ({i,j});
st[i][j] = true;
}
}
}
}
}
int main () {
cin >> n;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) cin >> h[i][j];
}
int peak = 0,valley = 0;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (!st[i][j]) {
bool has_higher = false,has_lower = false;
bfs (i,j,has_higher,has_lower);
if (!has_higher) peak++;
if (!has_lower) valley++;
}
}
}
cout << peak << ' ' << valley << endl;
return 0;
}
3.最短路模型
BFS最短路模型是一种基于广度优先搜索算法的最短路算法。它可以用于求解无权图中的最短路径问题,即从一个起点到所有其他点的最短路径。
BFS最短路模型的基本思想是从起点开始,依次遍历其周围的节点,并将其加入到队列中。然后依次取出队列中的节点,遍历其周围的节点,并将未被访问过的节点加入到队列中,同时记录下每个节点到起点的距离。如此循环,直到队列为空。
1.打印走出迷宫的最短路径
代码
#include <queue>
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int g[N][N];
PII memory[N][N];//第一次到达终点的路线一定是最短距离
int st[N][N];
int n;
void bfs()
{
queue<PII> q;
q.push({n - 1, n - 1});//从最后开始往起点走
st[n - 1][n - 1] = true;
int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};
while(q.size())
{
auto t = q.front();
q.pop();
for(int i = 0; i < 4; i ++)
{
int x = dx[i] + t.x, y = dy[i] + t.y;
if(x < 0 || x >= n || y < 0 || y >= n)continue;
if(st[x][y])continue;
if(g[x][y] == 1)continue;
q.push({x, y});
st[x][y] = true;
memory[x][y] = t;//记录的一定是最短路,每个点一定是距原点最近的距离,因为第二次到达该点的距离一定比第一次时的大,并且第二次到达该点直接跳过,并且该数组记录的是上一点的坐标(起点-->终点的路径,其数组指向的是该下标的下一步)
}
}
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++)
for(int j = 0; j < n; j ++)
cin >> g[i][j];
bfs();
PII end = {0, 0};
cout << 0 << ' ' << 0 << endl;
while(end.x != n - 1 || end.y != n - 1)
{
printf("%d %d\n", memory[end.x][end.y].x, memory[end.x][end.y].y);
int x = end.x, y = end.y;
end.x = memory[x][y].x, end.y = memory[x][y].y;
}
//该BFS是从终点开始遍历,因此到达起点的坐标的路线只有一条(第二次到达起点不会更新),因此不用担心输出memory的值不是最短路的值。
return 0;
}
2.从某一点到零一点的最短距离(有障碍)
没啥说的,学学结构体
#include <iostream>
#include <queue>
using namespace std;
int dx[] = {-2,-1,1,2,2,1,-1,-2},dy[] = {1,2,2,1,-1,-2,-2,-1};
const int N = 160;
struct point {
int x,y,step;
};
int n,m;
int sx,sy,ex,ey;
int g[N][N];
int bfs () {
queue <point> q;
q.push ({sx,sy,0});
while (!q.empty ()) {
point t = q.front ();
q.pop ();
if (t.x == ex && t.y == ey) return t.step;
for (int i = 0;i < 8;i++) {
int x = t.x+dx[i],y = t.y+dy[i];
if (x < 1 || x > n || y < 1 || y > m) continue;
if (g[x][y]) continue;
q.push ({x,y,t.step+1});
g[x][y] = 1;
}
}
return -1;
}
int main () {
cin >> m >> n;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
char ch;
cin >> ch;
if (ch == '*') g[i][j] = 1;
else if (ch == 'K') sx = i,sy = j;
else if (ch == 'H') ex = i,ey = j;
}
}
cout << bfs () << endl;
return 0;
}
3.可走长度不再是1的情况
思路:还是要求有一个st[]来判断该点是否走过,只有第一次到达该点时,到达该点的时间一定是最短的,一共有三种走法。
代码
#include<bits/stdc++.h>
using namespace std;
const int NN=1e5;
int n,k,sum[NN+4];
int bfs()
{
queue<int>q;
q.push(n);
while(q.size())
{
int t=q.front();
if(t==k)
return sum[t];
q.pop();
if(t+1<=NN&&t+1<=k&&!sum[t+1])//玄学优化
{
sum[t+1]=sum[t]+1;
q.push(t+1);
}
if(t-1>=0&&!sum[t-1])//玄学优化
{
sum[t-1]=sum[t]+1;
q.push(t-1);
}
if(t*2<=NN&&t*2-(k&1)<=k&&!sum[t*2])//玄学优化,肯定会有一个点*2-(k&1)在牛的位置-1,因此如果超过的话直接跳过就行,每必要放入队列中
{
sum[t*2]=sum[t]+1;
q.push(t*2);
}
}
}
int main()
{
scanf("%d%d",&n,&k);
if(n>k)//玄学优化
{
printf("%d",n-k);
return 0;
}
printf("%d",bfs());
return 0;
}
4.多源BFS
deque
在边权只能为0或1,有多个起点,当遍历每个点时,如果该方向的边权为0,则放在对头,如果为1,值加1后放在队尾。
这道题是没有0的边权,可以用队列
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e3 + 10;
int n, m;
char g[N][N];
int dist[N][N];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
void bfs()
{
queue<pair<int, int>> q;
memset(dist, -1, sizeof dist);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (g[i][j] == '1') {
q.push({i, j});//多个起点
dist[i][j] = 0;
}
}
}
//也可以,每个点遍历四个方向,合法就放进队列末尾,之后进行下一个点,也是可以求出最值
while (q.size()) {
auto t = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int x = t.first + dx[i], y = t.second + dy[i];
if (x < 0 || x >= n || y < 0 || y >= m) {
continue;
}
if (dist[x][y] == -1) {
dist[x][y] = dist[t.first][t.second] + 1;
q.push({x, y});
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> g[i];
}
bfs();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (j > 0) {
cout << " ";
}
cout << dist[i][j];
}
cout << endl;
}
return 0;
}