BFS--acwing

1.模板

BFS是广度优先搜索(Breadth-First Search)的缩写,是一种常用的图遍历算法。它从起点开始,先遍历所有与起点相邻的节点,再逐层向外遍历,直到遍历完整个图。

BFS算法可以用于许多问题,如查找图中两个节点之间的最短路径、生成所有可能的组合、求解迷宫等等。在实际应用中,BFS算法通常需要使用队列来实现。

使用BFS算法通常需要以下步骤:

  1. 定义输入和输出格式,例如图的邻接表表示、起点和终点等。
  2. 定义需要使用的变量,例如已访问节点集合、当前路径等。
  3. 实现BFS函数,在函数中进行BFS遍历,并根据具体问题进行相应的操作。BFS函数通常需要传入图、起点、终点等参数。
  4. 实现主函数,在主函数中调用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算法通常需要以下步骤:

  1. 定义输入和输出格式,例如图像矩阵、起点和目标颜色等。
  2. 定义需要使用的变量,例如已访问节点集合、当前路径等。
  3. 实现Flood Fill函数,在函数中进行Flood Fill操作,并根据具体问题进行相应的操作。Flood Fill函数通常需要传入图像矩阵、起点、目标颜色、填充颜色等参数。
  4. 实现主函数,在主函数中调用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;
}

5.最小步数模型

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值