【图论】Flood Fill

算法提高课笔记

目录

定义

什么是 Flood Fill 算法?

字面意思理解:洪水覆盖

也就是说,下图的格子分为两大类,一类比较高一类比较低,现在从任意一处较低的格子开始灌水,下一次水将会覆盖它相邻的、较低的格子,依此类推

这就相当于是BFS的思想(也可以用DFS实现,但BFS不会出现爆栈的问题)

Flood Fill 算法可以在线性时间复杂度内,找到某个点所在的连通块

例题

池塘计数

原题链接

农夫约翰有一片 N∗M 的矩形土地。

最近,由于降雨的原因,部分土地被水淹没了。

现在用一个字符矩阵来表示他的土地。

每个单元格内,如果包含雨水,则用"W"表示,如果不含雨水,则用"."表示。

现在,约翰想知道他的土地中形成了多少片池塘。

每组相连的积水单元格集合可以看作是一片池塘。

每个单元格视为与其上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。

请你输出共有多少片池塘,即矩阵中共有多少片相连的"W"块。

输入格式

第一行包含两个整数 N 和 M。

接下来 N 行,每行包含 M 个字符,字符为"W"或".",用以表示矩形土地的积水状况,字符之间没有空格。

输出格式

输出一个整数,表示池塘数目。

数据范围

1 ≤ N, M ≤ 1000

输入样例

cpp 复制代码
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

输出样例

cpp 复制代码
3

题意

模板题,问有多少连通块

思路

按顺序遍历图上每一个点,如果这个点是洼地且未被遍历过,就在答案的基础上加1,并且标记这个连通块的所有点,直到遍历完整个图

代码

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
#define ft first
#define sd second

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

int n, m;
char g[N][N]; // 存图
queue<PII> q;
bool st[N][N]; // 判重

void bfs(int x, int y)
{
    q.push({x, y});
    st[x][y] = true;

    while (q.size())
    {
        PII t = q.front();
        q.pop();

        for (int i = t.ft - 1; i <= t.ft + 1; i ++ )
            for (int j = t.sd - 1; j <= t.sd + 1; j ++ )
            {
                if (i == t.ft && j == t.sd) continue; // 正好遍历到当前点
                if (i < 0 || i >= n || j < 0 || j >= m) continue; // 位置不合法
                if (g[i][j] == '.' || st[i][j]) continue; // 不是洼地或已遍历过

                q.push({i, j});
                st[i][j] = true;
            }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

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

    int cnt = 0;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
            if (g[i][j] == 'W' && !st[i][j])
            {
                bfs(i, j);
                cnt ++ ;
            }
    cout << cnt << '\n';
}

城堡问题

原题链接

复制代码
    1   2   3   4   5   6   7  
   #############################
 1 #   |   #   |   #   |   |   #
   #####---#####---#---#####---#
 2 #   #   |   #   #   #   #   #
   #---#####---#####---#####---#
 3 #   |   |   #   #   #   #   #
   #---#########---#####---#---#
 4 #   #   |   |   |   |   #   #
   #############################
              (图 1)

   #  = Wall   
   |  = No wall
   -  = No wall

   方向:上北下南左西右东。

图1是一个城堡的地形图。

请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。

城堡被分割成 m∗n 个方格区域,每个方格区域可以有0~4面墙。

注意:墙体厚度忽略不计。

输入格式

第一行包含两个整数 m 和 n,分别表示城堡南北方向的长度和东西方向的长度。

接下来 m 行,每行包含 n 个整数,每个整数都表示平面图对应位置的方块的墙的特征。

每个方块中墙的特征由数字 P 来描述,我们用1表示西墙,2表示北墙,4表示东墙,8表示南墙,P 为该方块包含墙的数字之和。

例如,如果一个方块的 P 为3,则 3 = 1 + 2,该方块包含西墙和北墙。

城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。

输入的数据保证城堡至少有两个房间。

输出格式

共两行,第一行输出房间总数,第二行输出最大房间的面积(方块数)。

数据范围

1 ≤ m, n ≤ 50,

0 ≤ P ≤ 15

输入样例

cpp 复制代码
4 7 
11 6 11 6 3 10 6 
7 9 6 13 5 15 5 
1 10 12 7 13 7 5 
13 11 10 8 10 12 13 

输出样例

cpp 复制代码
5
9

题意

给出一个城堡中每个房间四面是否有墙壁,输出该城堡有多少个连通的房间,最大的连通块面积是多少

思路

和上一题基本一样,判断每个房间的四面墙壁情况,只是要额外加一个面积的计算

代码

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

const int N = 55;

typedef pair<int, int> PII;
#define ft first
#define sd second

int n, m;
int g[N][N]; // 存图
queue<PII> q;
bool st[N][N]; // 判重

int bfs(int x, int y)
{
    int area = 0;
    int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
    q.push({x, y});
    st[x][y] = true;
    while (q.size())
    {
        PII t = q.front();
        q.pop();
        area ++ ;

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.ft + dx[i], b = t.sd + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m) continue; // 位置不合法
            if (st[a][b]) continue; // 已被遍历
            if (g[t.ft][t.sd] >> i & 1) continue; // 这个方向是墙

            q.push({a, b});
            st[a][b] = true;
        }
    }
    return area;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    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 << '\n' << area;
}

山峰和山谷

原题链接

FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。

为了能够对旅程有一个安排,他想知道山峰和山谷的数量。

给定一个地图,为FGD想要旅行的区域,地图被分为 n×n 的网格,每个格子 (i,j) 的高度 w(i,j) 是给定的。

若两个格子有公共顶点,那么它们就是相邻的格子,如与 (i,j) 相邻的格子有(i−1,j−1),(i−1,j),(i−1,j+1),(i,j−1),(i,j+1),(i+1,j−1),(i+1,j),(i+1,j+1)。

我们定义一个格子的集合 S 为山峰(山谷)当且仅当:

  1. S 的所有格子都有相同的高度。
  2. S 的所有格子都连通。
  3. 对于 s 属于 S,与 s 相邻的 s′ 不属于 S,都有 ws>ws′(山峰),或者 ws<ws′(山谷)。
  4. 如果周围不存在相邻区域,则同时将其视为山峰和山谷。

你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。

输入格式

第一行包含一个正整数 n,表示地图的大小。

接下来一个 n×n 的矩阵,表示地图上每个格子的高度 w。

输出格式

共一行,包含两个整数,表示山峰和山谷的数量。

数据范围

1 ≤ n ≤ 1000,

0 ≤ w ≤ 109

输入样例1

cpp 复制代码
5
8 8 8 7 7
7 7 8 8 7
7 7 7 7 7
7 8 8 7 8
7 8 8 8 8

输出样例1

cpp 复制代码
2 1

输入样例2

cpp 复制代码
5
5 7 8 3 1
5 5 7 6 6
6 6 6 2 8
5 7 2 5 8
7 1 0 1 7

输出样例2

cpp 复制代码
3 3

题意

给出不同地块的高度,高度相同的连通块周边高度都小于等于该连通块高度,那么该连通块为山峰,反之为山谷,问山峰山谷数量

思路

依旧是遍历每一个点,如果没有遍历过该点,就遍历该点所在连通块,直到找到连通块边界,再判断这个连通块是否为山峰或山谷

代码

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

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

typedef pair<int, int> PII;
#define ft first
#define sd second

int n;
int h[N][N];
queue<PII> q;
bool st[N][N];

void bfs(int x, int y, bool& has_higher, bool& has_lower)
{
    q.push({x, y});
    st[x][y] = true;

    while (q.size())
    {
        PII t = q.front();
        q.pop();

        for (int i = t.ft - 1; i <= t.ft + 1; i ++ )
            for (int j = t.sd - 1; j <= t.sd + 1; j ++ )
            {
                if (i == t.ft && j == t.sd) continue; // 遍历到自身
                if (i < 0 || i >= n || j < 0 || j >= n) continue; // 位置不合法
                if (h[i][j] != h[t.ft][t.sd]) // 边界
                {
                    if (h[i][j] > h[t.ft][t.sd]) has_higher = true;
                    else has_lower = true;
                }
                else if (!st[i][j]) // 非边界且未遍历
                {
                    q.push({i, j});
                    st[i][j] = true;
                }
            }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    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 << '\n';
}
相关推荐
CoovallyAIHub2 分钟前
南京理工大学联手百度、商汤科技等团队推出Artemis:用结构化视觉推理革新多模态感知
深度学习·算法·计算机视觉
天才少女爱迪生5 分钟前
图像序列预测有什么算法方案
人工智能·python·深度学习·算法
cici158747 分钟前
3D有限元直流电阻率法正演程序
算法·3d
黑色的山岗在沉睡11 分钟前
滤波算法数学前置——线性化
线性代数·算法
t1987512814 分钟前
火电机组热经济性分析MATLAB程序实现
人工智能·算法·matlab
Hello娃的22 分钟前
【半导体】肖特基接触AND欧姆接触
人工智能·算法
橘颂TA23 分钟前
【剑斩OFFER】算法的暴力美学——交易逆序对的总数
数据结构·算法·leetcode
xiyuping2426 分钟前
强化学习之——moutaincar
算法·机器学习
小画家~28 分钟前
第三十七:类型断言
开发语言·c++·算法·golang
九年义务漏网鲨鱼31 分钟前
【大模型微调】QLoRA微调原理及实战
深度学习·算法·大模型·智能体