算法百题斩其一: 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;
}