深度优先搜索(续)
Posted 中学生编程与信息学竞赛自学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度优先搜索(续)相关的知识,希望对你有一定的参考价值。
注意:本文用到了初中的数学知识以及C++的编程代码。
编程学习最好使用计算机, 请登陆 www.3dian14.org 学习更多的编程知识。
今天继续看一个深度优先搜索的经典例子。
在一次降雨中,雨水汇集在某块农田中,该农田由N×M(1 <= N <= 100; 1 <= M <= 100)小正方形方格组成的矩形。每个方格或者其中有积水(用“W”表示)或者还是干地(用“.”表示)。现在想弄清楚农田里到底形成了多少个池塘。池塘是一组相互连接的方格,每个方格中都有水,注意这里每个方格与它相邻的八个方格都看成是连接的。对于给定的田地的图形,请找出其中有多少个池塘。
输入:
第1行:两个以空格分隔的整数:N和M,分别表示该田地由N×M个方格组成。
第2..N + 1行:每行M个字符,分别对应田地的一行方格。每个字符都是“W”或“.”。字符之间没有空格。
输出:农田中的池塘数量。
下面是一种输入样例:
依据池塘的定义,对于有水的W方格S,相邻的左上,上方、右上、左方、右方、左下、下方、右下的任何一个有W的方格,都是与S连通的,都是属于同一个池塘的。可以看出在其中包含有三个池塘,见下图所示。
解题思路
池塘其实是连通的积水格子。
我们采用深度优先DFS的算法。从第一行第一列的方格开始,依次对W方格利用深度优先法DFS进行检测。
对于某个W方格S1,S1必定属于一个池塘,我们只要依次检查位于S1左上,上方、右上、左方、右方、左下、下方、右下的邻居方格,看它们是否也是W。我们首先把S1涂成“.”,表示它已经检测过了,接着对于S1的每一个W邻居N,也采用同样的方法进行检测,也就是首先把N也从“W”涂成“.”,表示已经检查过了,也按同样依次检查N的邻居。
当一个W方格的所有W邻居都已是“.”了,则返回上层继续检测。这样可以把与S1连通的所有积水格子都设置成“.”。最后统计一共进行了多少次DFS计算,就是池塘的个数。
算法复杂度
每个格子有八个方向,每个方向对应一种状态转移,每次状态转移时,对每个格子最多检测一次,因此复杂度是O(8*N*M),也就是O(N*M)。
程序实现
下面是C/C++的类代码:
//输入
int N,M;
char field[N_LIMIT][M_LIMIT];//数组,存放对应方格当前状态(W或.)
//当前检测的方格坐标(x,y),x的取值为0..N-1, y的取值为0..M-1,对应第x+1行、y+1列的方格
void dfs(int x, int y)
{
field[x][y] = '.'; //将当前位置“W”设为“.”,表示已经访问过,下次无须再访问。
//对应八个方向,循环检测
for (int dx=-1; dx<=1; dx++)
{
for (int dy=-1; dy<=1; dy++)
{
// 邻居坐标为(n_x,n_y)
int n_x = x+dx;
int n_y = y+dy;
//判断邻居方格坐标(n_x,n_y)是否越界
if (0<=n_x && n_x<N && 0<=n_y && n_y<M)
{
//如果该邻居是W,则表示需要进一步对它进行探测
if (field[n_x][n_y]=='W')
dfs(n_x,n_y);
}
}
}
return;
}
void pondnum()
{
int num = 0; //连通水域的池塘个数
}
for (int i=0;i<N;i++){
for (int j=0;i<M;j++){
//如果找到一个W的积水格子,则采用深度优先算法探索所有与它相连通的格子
if(field[i][j]=='W'){
dfs(i,j);
num++;//统计顶层所有调用dfs的次数,也就是连通水域的池塘个数
}
}
}
printf("%d
",num);
}
以上是关于深度优先搜索(续)的主要内容,如果未能解决你的问题,请参考以下文章
数据结构与算法图遍历算法 ( 深度优先搜索 DFS | 深度优先搜索和广度优先搜索 | 深度优先搜索基本思想 | 深度优先搜索算法步骤 | 深度优先搜索理论示例 )