云计算百科
云计算领域专业知识百科平台

GESP6级C++考试语法知识(泛洪算法(Flood Fill))


图像里的魔法——泛洪算法(Flood Fill)


一、先讲一个生活中的故事

小明有一张黑白地图:

########
#……#
#.####.#
#.#..#.#
#……#
########

其中:

  • # 表示墙

  • . 表示空地

现在小明拿着一桶颜料:

点击一个地方

把和它连在一起的所有空地染成红色

这是不是很像:

画图软件里的“油漆桶工具”?

比如:

你点击一个区域:

……..
..####..
..#..#..
..####..
……..

油漆会自动扩散:

RRRRRRRR
RR####RR
RR#..#RR
RR####RR
RRRRRRRR

这个过程,就是:

Flood Fill(泛洪算法)


二、泛洪算法解决什么问题?

一句话:

从一个点出发,把所有“连通”的相同区域找出来。

关键词:

1. 从一个点开始

例如:

(2,3)

2. 向四周扩散

看看:




3. 如果符合条件,继续扩散

像水流一样:


|
← ← 起点 → →
|


三、二维地图怎么表示?

计算机里面:

地图就是二维数组。

例如:

1 1 1 1 1
1 0 0 1 1
1 0 1 1 1
1 0 0 0 1
1 1 1 1 1

C++:

int mp[5][5];

表示:

mp[行][列]

例如:

mp[2][3]

就是:

第2行,第3列。


四、泛洪算法的核心思想

假设:

0代表土地

1代表障碍

地图:

0 0 0 1 1
0 0 1 1 1
0 0 0 0 1
1 1 0 0 0

从:

(0,0)

开始。

我们想:

把所有和它连接的0变成2。

过程:

第一步

染当前位置:

2 0 0 1 1
0 0 1 1 1
0 0 0 0 1
1 1 0 0 0

然后检查:





第二步

向右:

2 2 0 1 1
0 0 1 1 1
0 0 0 0 1
1 1 0 0 0

继续:

2 2 2 1 1
0 0 1 1 1
0 0 0 0 1
1 1 0 0 0

直到所有连接区域完成。


五、DFS实现泛洪算法(最经典)

1. 定义方向

因为只能走:




所以:

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

解释:

第0种:

x-1,y

向上

第1种:

x+1,y

向下

第2种:

x,y-1

向左

第3种:

x,y+1

向右


六、写DFS函数

模板:

void dfs(int x,int y)
{

}

表示:

现在站在:

(x,y)

这个位置。


第一步:染色

为什么?

因为:

如果不标记,

会无限循环。

例如:

A -> B

B -> A

A -> B

所以:

进入一个点:

马上标记。

代码:

mp[x][y]=2;


第二步:尝试四个方向

for(int i=0;i<4;i++)
{

}


第三步:计算新坐标

int nx=x+dx[i];
int ny=y+dy[i];

比如:

现在:

x=3
y=4

向上:

nx=2
ny=4


第四步:判断能不能走

需要满足:

条件1:

不能越界

例如:

-1行

不存在。

所以:

nx>=0


条件2:

必须是目标颜色

比如:

只能走0:

mp[nx][ny]==0


完整:

if(nx>=0&&nx<n
&&ny>=0&&ny<m
&&mp[nx][ny]==0)
{
dfs(nx,ny);
}


七、完整C++代码

题目:

把和起点连通的0全部变成2。

#include<iostream>
using namespace std;

int n,m;

int mp[100][100];

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

void dfs(int x,int y)
{

//1.染色
mp[x][y]=2;

//2.寻找四个方向
for(int i=0;i<4;i++)
{

int nx=x+dx[i];
int ny=y+dy[i];

//3.判断是否可以继续
if(nx>=0&&nx<n
&&ny>=0&&ny<m
&&mp[nx][ny]==0)
{
dfs(nx,ny);
}

}

}

int main()
{

cin>>n>>m;

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

int x,y;

cin>>x>>y;

dfs(x,y);

for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cout<<mp[i][j]<<" ";
}

cout<<endl;
}

return 0;
}


八、学生最容易犯的3个错误

错误1:忘记标记

错误:

void dfs(int x,int y)
{

dfs(nx,ny);

}

没有:

mp[x][y]=2;

结果:

无限递归。


为什么?

例如:

A B
C D

A走B:

A→B

B又能走A:

B→A

死循环。


错误2:方向数组写错

很多孩子写:

int dx[4]={1,1,-1,-1};

这是什么?

变成:




走斜线。

如果题目要求上下左右:

必须:

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


错误3:边界忘记判断

例如:

第0行

再向上:

-1行

数组:

mp[-1][0]

非法。

程序可能:

  • 崩溃

  • 输出奇怪结果


九、DFS和BFS有什么区别?

泛洪算法有两种写法:

DFS

深度优先搜索:

像一个小朋友探险:

一直走到底

走不通回来

特点:

代码短。

适合:

  • 区域染色

  • 连通块


BFS

广度优先搜索:

像水波:

第一圈

第二圈

第三圈

使用:

队列 queue。

适合:

  • 最短距离

  • 迷宫最短路


十、信奥赛中的经典应用

泛洪算法非常重要。

以后会遇到:


1. 统计岛屿数量

例如:

11000
11000
00100
00011

有几个岛?

答案:

每找到一个1:

DFS染掉。


2. 迷宫连通性

判断:

起点能不能到终点


3. 最大区域面积

例如:

0 1 1
1 1 0
1 0 0

找最大连通块。


4. 填充地图

类似:

画图软件油漆桶


十一、给同学们总结一句话

泛洪算法就是:
从一个位置出发,像水一样向四周流动,把所有能够到达的地方全部处理一遍。

记住三个关键:

第一:

二维数组表示地图。

第二:

DFS/BFS负责扩散。

第三:

一定要:

走一步,标记一步。


对于信奥赛学生来说,泛洪算法其实是进入 DFS搜索、连通块、图论 的第一座桥梁。掌握它以后,后面的 迷宫问题、岛屿问题、图的遍历、BFS最短路 都会顺畅很多。

赞(0)
未经允许不得转载:网硕互联帮助中心 » GESP6级C++考试语法知识(泛洪算法(Flood Fill))
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!