AcWing.算法提高课_BFS中的Flood Fill和最短路模型

BFS性质

(1)便于“求最小”;第一次到达某点为到达该点的最短路径
(2)基于迭代,不会“爆栈”

BFS主要求解问题种类

(1)最短距离:地图某一点到另一点的最短距离
(2)最小步数:地图某一种状态到另一种状态的最小步数

模型一:Flood Fill

特点:可以在线性时间复杂度内找到某点所在的连通块

主要解决问题:计算连通块数量&大小

例题一

AcWing.1097池塘计数
题目要求:计算连通块个数
思路:遍历池塘中的每个点,若为池塘,则池塘数量加一,后找出并标记该池塘的所有位置
实现代码

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

using namespace std;

#define x first
#define y second

const int N = 1010;

typedef pair<int,int> PII;

int n,m;
char g[N][N];
int cnt;
bool st[N][N];

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

void bfs(int a, int b){
    cnt ++;

    queue<PII> q;

    q.push({a,b});
    while(q.size()){
        auto t = q.front();
        q.pop();

        for(int i = 0; i < 8; i ++)
            for(int j = 0; j < 8; j ++){
                int x = t.x + dx[i], y = t.y + dy[i];
                if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 'W'){
                    q.push({x,y});
                    g[x][y] = '.';//防止重复查找
                }
            }
    }
}

int main()
{
    cin>>n>>m;
    for(int i = 0; i < n; i ++) scanf("%s",g[i]);

    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
            if(g[i][j] == 'W') bfs(i,j);

    cout<<cnt<<endl;

    return 0;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3877790/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
例题二

AcWing1098.城堡问题
题目要求计算连通块个数与大小
思路
在这里插入图片描述
实现代码

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

using namespace std;

const int N = 55,M = N * N;

typedef pair<int,int> PII;

#define x first
#define y second

int n, m;
int g[N][N];
PII q[M];
bool st[N][N];

int bfs(int sx, int sy){
    int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};

    int hh = 0, tt = -1;
    q[++ tt] = {sx, sy};
    st[sx][sy] = true;

    int area = 0;

    while(hh <= tt){
        PII t = q[hh ++];
        area ++;

        for(int i = 0; i < 4; i ++){
            int a = t.x + dx[i], b = t.y + dy[i];
            if(a < 0 || a >= n || b < 0 || b >= m) continue;
            if(st[a][b]) continue;
            if(g[t.x][t.y] >> i & 1) continue;

            q[++ tt] = {a, b};
            st[a][b] = true;
        }
    }

    return area;
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
            cin >> g[i][j];

    int cnt = 0, area = 0;
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
            if(!st[i][j]){
                area = max(area, bfs(i,j));
                cnt ++;
            }

    cout << cnt << endl << area << endl;

    return 0;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3973495/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
例题三

AcWing1106.山峰与山谷
题目要求判断某连通块是否符合要求并计算连通块个数
思路:遍历每个点,找出该连通块中的所有点并标记,若符合要求则计数

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

using namespace std;

const int N = 1010,M = N * N;

#define x first
#define y second

typedef pair<int,int> PII;

int n;
int h[N][N];
bool st[N][N];
PII q[M];

void bfs(int sx, int sy, bool& has_higher, bool& has_lower){
    int hh = 0, tt = -1;
    q[++ tt] = {sx, sy};
    st[sx][sy] = true;

    while(hh <= tt){
        PII t = q[hh ++];

        for(int i = t.x - 1; i <= t.x + 1; i ++)
            for(int j = t.y - 1; j <= t.y + 1; j ++){
                if(i == t.x && j == t.y) continue;
                if(i < 0 || i >= n || j < 0 || 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[++ tt] = {i, j};
                    st[i][j] = true;
                }
            }
    }
}

int main()
{
    cin >> n;

    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            cin >> h[i][j];

    int peak = 0, valley = 0;
    for(int i = 0;i < n; i ++)
        for(int j = 0; 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;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3970289/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

模型二:最短路模型

BFS->搜到某点时,得到起点到该点的最短路
->线性时间复杂度得到所有点到起点的最短路
需要满足:所有边权重相等
主要解决问题:到达某点的最短距离

例题一

AcWing1076.迷宫问题
题目要求到达地图某点的最短距离以及最短路
解题关键点or技巧
(1)记录最短路路径:标记到达每个点的前一个点,由此倒推得最短路径
(2)从(n-1,n-1)位置倒推至起点->倒推得最短路径时为从真实起点出发

(1)下一步:dx[] = {-1, 0, 1, 0};dy[] = {0, 1, 0, -1};
(2)判断条件
①越出边界不合法:if(a < 0 || a >= n || b < 0 || b >= n) continue;
②遍历过不合法:if(pre[a][b].x != -1) continue;
③地图上是墙不合法:if(g[a][b]) continue;

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

using namespace std;

const int N = 1010, M = N * N;

#define x first
#define y second

typedef pair<int,int> PII;

int n;
int g[N][N];
PII q[M];
PII pre[N][N];

void bfs(int sx, int sy){
    int dx[] = {-1, 0, 1, 0},dy[] = {0, 1, 0, -1};

    int hh = 0, tt = -1;
    q[++ tt] = {sx,sy};

    memset(pre, -1, sizeof pre);
    pre[sx][sy] = {0, 0};

    while(hh <= tt){
        PII t = q[hh ++];

        for(int i = 0; i < 4; i ++){
            int a = t.x + dx[i], b = t.y + dy[i];

            if(a < 0 || a >= n || b < 0 || b >= n) continue;
            if(pre[a][b].x != -1) continue;
            if(g[a][b]) continue;

            q[++ tt] = {a,b};
            pre[a][b] = t;
        }
    }
}

int main()
{
    cin >> n;

    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            cin >> g[i][j];

    bfs(n - 1, n - 1);

    PII end(0, 0);

    while(1){
        cout << end.x << ' ' << end.y << endl;
        if(end.x == n - 1 && end.y == n - 1) break;
        end = pre[end.x][end.y];
    }

    return 0;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3971429/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
例题二

AcWing188.武士风度的牛
题目要求到达地图某点的最短距离

(1)下一步:dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};dy[] = {1, 2, 2, 1, -1, -2, -2, -1};
(2)判断条件
①越出边界不合法:if(a < 0 || a >= n || b < 0 || b >= m) continue;
②题目给出障碍物不合法:if(g[a][b] == ‘*’) continue;
③遍历过不合法:if(dist[a][b] != -1) continue;

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

using namespace std;

const int N = 155, M = N * N;

#define x first
#define y second

typedef pair<int,int> PII;

int n,m;
char g[N][N];
PII q[M];
int dist[N][N];

int bfs(){
    int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
    int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};

    int sx, sy;

    for(int i = 0; i < n; i ++)
        for(int j = 0; j < m; j ++)
            if(g[i][j] == 'K')
                sx = i, sy = j;

    int hh = 0, tt = -1;
    q[++ tt] = {sx, sy};

    memset(dist, -1, sizeof dist);
    dist[sx][sy] = 0;

    while(hh <= tt){
        PII t = q[hh ++];

        for(int i = 0; i < 8; i ++){
            int a = t.x + dx[i], b = t.y + dy[i];

            if(a < 0 || a >= n || b < 0 || b >= m) continue;
            if(g[a][b] == '*') continue;
            if(dist[a][b] != -1) continue;
            if(g[a][b] == 'H') return dist[t.x][t.y] + 1;

            dist[a][b] = dist[t.x][t.y] + 1;
            q[++ tt] = {a, b};
        }
    }

    return -1;
}

int main()
{
    cin >> m >> n;

    for(int i = 0; i < n; i ++) cin >> g[i];

    cout << bfs() << endl;

    return 0;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3971694/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
例题四

AcWing 1100.抓住那头牛
题目要求计算到达一维坐标系某点的最短距离

(1)下一步:(x + 1)、(x - 1)、(x * 2)
(2)判断条件
①x - 1:(x - 1 >= 0 && dist[x - 1] == -1)
②x + 1:(x + 1 < N && dist[x + 1] == -1)
③x * 2:(x * 2 < N && dist[x * 2] == -1)

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

using namespace std;

const int N = 100010;

int n,k;
int q[N];
int dist[N];

int bfs(){
    memset(dist, -1, sizeof dist);

    int hh = 0, tt = -1;
    q[++ tt] = n;

    dist[n] = 0;

    while(hh <= tt){
        int x = q[hh ++];

        if(x == k) return dist[k];

        if(x - 1 >= 0 && dist[x - 1] == -1){
            q[++ tt] = x - 1;
            dist[x - 1] = dist[x] + 1;
            // dist[x - 1] = dist[x] + 1;
            // q[++ tt] = x - 1;
        }
        if(x + 1 < N && dist[x + 1] == -1){
            q[++ tt] = x + 1;
            dist[x + 1] = dist[x] + 1;
            // dist[x + 1] = dist[x] + 1;
            // q[++ tt] = x + 1;
        }
        if(x * 2 < N && dist[x * 2] == -1){
            q[++ tt] = x * 2;
            dist[x * 2] = dist[x] + 1;
            // dist[x * 2] = dist[x] + 1;
            // q[++ tt] = x * 2;
        }
    }

    return -1;
}

int main()
{
    cin >> n >> k;

    cout << bfs() << endl;

    return 0;
}

作者:Duktig_7
链接:https://www.acwing.com/activity/content/code/content/3973170/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值