# 算法百题斩其一: floodfill

算法百题斩其一: floodfill

写在前面:何所谓“斩”?
斩,即快速而有力地切断,指我们用最精简的语言,一针见血地点破算法题的核心难点。斩需三思而后行;斩需借助外力、旁征博引;斩需持之以恒、铁杵磨针!
1. 何为floodfill?

floodfill算法,中文名泛洪填充算法。顾名思义,即枚举图中每一个没有被填充的方块,以某种连通性规则去填充一片区域,迭代进行下去。(就好比你在某个格子放置一个水方块,然后其水淹四方一样,MC玩家狂喜~)
floodfill是一种搜索算法,可以用dfs或者bfs实现floodfill。floodfill一般用来遍历一张图,求连通块的个数

2.例题

例题一
在这里插入图片描述
在这里插入图片描述

分析+代码:

在这里插入代码片//floodfill模板题: 在八连通规则下求连通块个数
//要点:1.若用数组实现队列,bfs要开一个足够容纳所有状态的数组
//要点:2.双层循环加扣点实现八连通规则下的延申

#include <iostream>

using namespace std;

#define x first//宏定义方便代码撰写
#define y second
typedef pair<int,int> PII;//定义一个pair类型在bfs中存二维点
const int N=1010,M=N*N;

PII q[M];//!!注意队伍需要开n方的空间!!
char g[N][N];//存图
bool st[N][N];//bfs所需判重数组
int n,m,hh,tt;//队列头尾下标

void bfs(int r,int c)
{
    hh=tt=0;
    q[0]={r,c};
    st[r][c]=true;
    while(tt>=hh)
    {
        int x=q[hh].x, y=q[hh++].y;
        for(int i=x-1;i<=x+1;i++)
            for(int j=y-1;j<=y+1;j++)
            {
                if(st[i][j])continue;
                if(i==x&&j==y)continue;
                if(i<0||i>=n||j<0||j>=m)continue;
                if(g[i][j]=='.')continue;
                q[++tt]={i,j};
                st[i][j]=true;
            }
        
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>g[i];
    int ans=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(st[i][j]||g[i][j]=='.')continue;
            bfs(i,j);
            ans++;
        }
    cout<<ans<<endl;
}

例题二
在这里插入图片描述
在这里插入图片描述

分析+代码:

//floodfill模板题: 在利用二进制数表示连通性的图中进行floodfill,并统计连通块个数和最大连通块面积(一个n位二进制数唯一对应n个方向的墙存在与否的一种情况)
//要点:1.用数组实现四连通延申
//细节:1.#define 的用法,把用来替换的放在前面,被替换的关键词放在后面,不要加分号
#include <iostream>
#include <algorithm>

using namespace std;
typedef pair<int,int>PII;
#define x first
#define y second

const int N=55;
int g[N][N],dx[4]={0,-1,0,1},dy[4]={-1,0,1,0},hh,tt,m,n;//m是行数
PII q[N*N];
bool st[N][N];

int bfs(int i,int j)
{
    hh=tt=0;
    q[0]={i,j};
    st[i][j]=true;
    int area=1;
    
    while(hh<=tt)
    {
        int x=q[hh].x,y=q[hh++].y;
        int code=g[x][y];
        for(int k=0;k<4;k++)
        {
            if(!(code>>k&1))
            {
                int x1=x+dx[k],y1=y+dy[k];
                if(st[x1][y1]||x1<0||x1>=m||y1<0||y1>=n)continue;
                area++;
                q[++tt]={x1,y1};
                st[x1][y1]=true;
            }
        }
    }
    return area;
}

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

例题三
在这里插入图片描述
在这里插入图片描述

//具有一种特殊连通规则(数值相等)的8连通floodfill,同时需要统计连通块的相邻块
#include <iostream>
#include <algorithm>

using namespace std;

const int N=1e3+10,M=1e6+10;
typedef pair<int,int> PII;
#define x first
#define y second

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

void bfs(int r,int c,int &mount,int &valley)
{
    hh=tt=0; 
    q[hh]={r,c};
    st[r][c]=true;
    bool mou=true,val=true;
    
    while(hh<=tt)
    {
        int x=q[hh].x,y=q[hh++].y;
        for(int i=x-1;i<=x+1;i++)
            for(int j=y-1;j<=y+1;j++)
            {
                if(i<0||i>=n||j<0||j>=n)continue;
                if(i==x&&j==y)continue;
                if(g[i][j]!=g[x][y])
                {
                    if(g[i][j]>g[x][y])mou=false;
                    else val=false;
                    continue;
                }
                if(st[i][j])continue;
                st[i][j]=true;
                q[++tt]={i,j};
            }
    }
    mount+=mou;
    valley+=val;
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>g[i][j];
    
    int mount=0,valley=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(!st[i][j])bfs(i,j,mount,valley);
        }
        
    cout<<mount<<" "<<valley<<endl;
            
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值