Flood Fill Lv.2
这难度要都是这样我一天能干十节
一、算法介绍
想象一个这样的模型:有一张地图,上面有一些洼地,有一些高地。任选一个点,在其上方悬挂一个无尽水源,开始往这个格子里灌水。每过一个单位时间,水会漫进周围的格子,只能覆盖洼地,不能覆盖高地,最终整块区域的洼地都被水填满。这样的模型我们称为
当然,
那这个算法有什么用呢?它可以在线性时间内求出在经过一段时间后,某个点所在的连通块。
二、\mathrm{Flood\space Fill} 通用模板
#include <bits/stdc++.h>
using namespace std;
const int N=10010;
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m;
int g[N][N];
bool st[N][N];
void bfs(int sx,int sy)
{
queue <PII> q;
q.push({sx,sy});
st[sx][sy]=true;
while (q.size())
{
PII t=q.front();
q.pop();
// On 8-connected graph :
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<1 || i>n || j<1 || j>m)
continue;
if (st[i][j])
continue;
// other judgement
// do something ...
q.push({i,j});
st[i][j]=true;
}
// On 4-connected graph :
for (int i=0;i<4;i++)
{
int a=t.x+dx[i],b=t.y+dy[i];
// repeat steps above
}
}
return;
}
int main()
{
// input and process ...
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (!st[i][j])
{
// do something ...
bfs(i,j);
// do something ...
}
// process and output ...
}
这些在后文的例题中基本都没有变化,最多因题目而对顺序进行一些微小的调整。
三、\mathrm{Flood\space Fill} 算法的应用
1. 应用 1 :计算地图中连通块的数量
我们从上到下、从左到右地遍历整张地图,发现了是我们该统计的区域,并且该点还没有被访问过的话,我们就从这个格子开始进行
I. 例题 1. 池塘计数
模板题,注意在写搜索有关的题目都要注意各种边界细节问题!
代码实现:
// AcWing 1097. 池塘计数
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair <int,int> PII;
const int N=1010;
int n,m,ans;
char g[N][N];
bool st[N][N];
void bfs(int x,int y)
{
queue <PII> q;
q.push({x,y});
st[x][y]=true;
while (q.size())
{
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==t.x && j==t.y)
continue;
if (i<1 || i>n || j<1 || j>m)
continue;
if (g[i][j]!='W' || st[i][j])
continue;
q.push({i,j});
st[i][j]=true;
}
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%s",g[i]+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (g[i][j]=='W' && !st[i][j])
{
bfs(i,j);
ans++;
}
printf("%d\n",ans);
return 0;
}
2. 应用 2 :求每个连通块的大小
I. 例题 2. 城堡问题
唯一的不同在于对输入数据的处理和求出最大面积,但这些对能学到这里的我们来说都是小 (逃
代码实现:
// AcWing 1098. 城堡问题
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair <int,int> PII;
const int N=60;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m;
int g[N][N];
bool st[N][N];
int bfs(int sx,int sy)
{
queue <PII> q;
q.push({sx,sy});
st[sx][sy]=true;
int area=0;
while (q.size())
{
PII t=q.front();
q.pop();
area++;
for (int i=0;i<4;i++)
{
int a=t.x+dx[i],b=t.y+dy[i];
if (a<1 || a>n || b<1 || b>m)
continue;
if (st[a][b])
continue;
if (g[t.x][t.y]>>i&1)
continue;
q.push({a,b});
st[a][b]=true;
}
}
return area;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&g[i][j]);
int area=0,cnt=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (!st[i][j])
{
area=max(area,bfs(i,j));
cnt++;
}
printf("%d\n%d\n",cnt,area);
return 0;
}
3. 应用 3 :处理连通块与周围部分的关系
I. 例题 3. 山峰和山谷
代码实现:
// AcWing 1106. 山峰和山谷
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair <int,int> PII;
const int N=1010;
int n;
int g[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});
st[sx][sy]=true;
while (q.size())
{
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==t.x && j==t.y)
continue;
if (i<1 || i>n || j<1 || j>n)
continue;
if (g[i][j]!=g[t.x][t.y])
{
if (g[i][j]>g[t.x][t.y])
has_higher=true;
else
has_lower=true;
}
else if (!st[i][j])
{
q.push({i,j});
st[i][j]=true;
}
}
}
return;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%d",&g[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++;
}
printf("%d %d\n",peak,valley);
return 0;
}