时间限制:1 秒 内存限制:32 兆 特殊判题:否
题目描述:
The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.
输入:
The input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either'*'
, representing the absence of oil, or'@'
, representing an oil pocket.
输出:
For each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets.
样例输入:
1 1
*
3 5
@@*
@
@@*
1 8
@@***@
5 5
****@
@@@
@**@
@@@@
@@**@
0 0
样例输出:
0
1
2
2
题目大意:在给定的 n*m 图中,确定有几个@的块。块符合以下条件,其中的任意对@均互相直接或间接连通,两个@直接相邻或者对角相邻即被视为连通。
我们可以这样解决这个问题,首先对图上所有位置均设置一个标记位,表示该位置是否已经被计算过,且该标记仅对地图上为@的点有效。这样我们按从左至右、从上往下的顺序依次遍历地图上所有位置,若遍历到@,且该点未被标记,则所有与其直接相邻、或者间接相邻的@点与其一起组成一个块,该块即为一个我们需要计算的块,将该块中所有的@位置标记为已经计算。这样,当所有的位置被遍历过后,我们即得到了所需的答案。
#include
char maze[101][101];//保存地图信息
bool mark[101][101];//为图上每个点设立一个状态
int n,m;//地图大小为n*m
int go[][2]={
1,0,
-1,0,
0,1,
0,-1,
1,1,
1,-1,
-1,-1
,-1,1
};//八个相邻点与当前位置的坐标差
void DFS(int x,int y){
//递归遍历所有与x,y直接或间接相邻的@
for(int i=0;i<8;i++){
//遍历八个相邻点
int nx=x+go[i][0];
int ny=y+go[i][1];//计算其坐标
if(nx<1 || nx>n || ny<1 || ny>m)continue;
//若该坐标在地图外
if(maze[nx][ny]=='*')continue;//若该位置不是@
if(mark[nx][ny]==true)continue;//若该位置已经被计算过
mark[nx][ny]=true;//标记该位置为已经计算
DFS(nx,ny);//递归查询与该相邻位置直接相邻的点
}
return;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0 && m==0)break;
for(int i=1;i<=n;i++){
scanf("%s",maze[i]+1);
//第i行地图信息保存在maze[i][1]到maze[i][m]中
}//按行为单元输入地图信息
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mark[i][j]=false;
}
}//初始化所有位置为未被计算
int ans=0;//初始化块计数器
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//按顺序遍历图中所有位置
if(mark[i][j]==true)continue;
//若该位置已经被处理,跳过
if(maze[i][j]=='*')continue;
//若该位置不为@,跳过
DFS(i,j);//递归遍历与其直接或间接相邻的@
ans++;//答案递增
}
}
printf("%d\n",ans);//输出
}
return 0;
}
这类将相邻的点联合成一个块的算法有一个专门的名词——Flood Fill,它常作为某些算法的预处理方式使用。
读者可以联想到,用之前所讲的广度优先搜索也可以完成此例题。但是递归的优势就在于其精简的代码,与 BFS 动则百行的代码量相比,递归精巧简练的代码值得我们在机试中使用。
在结束对递归的讨论之前,我们还需要特别的强调,使用递归函数务必注意递归的层数。一个程序可以使用的栈空间是有限的,当递归的过深或者每层递归所需的栈空间太大将会造成栈的溢出,使评判系统返回程序运行时异常终止的结果,一旦你的递归程序出现了这种错误,你就要考虑是否是由递归的太深而造成了爆栈。这是使用递归程序一个很重要的注意要点。具体可使用的栈大小,因个评判系统不同而有所差异,需要读者自行测试后确定。