首先是DFS模板套路:
void DFS(...)//v是顶点
{
访问结点相关操作;
for(从结点的第一个相邻接点;结点没有相邻接点了即终止条件;下一个相邻结点)
{
if(相邻结点满足条件如未访问过)
{
对相邻结点调用DFS();
}
}
return;
}
int main()
{
初始化是否访问标记;
for(int v=xxx;v<Vnum;v++){//xxx表示从第xxx个顶点开始遍历,第xxx个结点是一个连通分量的任一结点。
//调用一次DFS为访问了图的一个连通分量的所有结点
//若已知图为连通的,则不需for循环,直接调用DFS()即可
if(满足条件如为访问过)
{
调用DFS();
}
}
return 0;
}
例题1:油田(Oil Deposits, UVa 572,ZOJ1709,POJ1562)
输入一个m行n列的字符矩阵,统计字符“@”组成多少个八连块。如果两个字符“@”所在的格子相邻(横、竖或者对角线方向),就说它们属于同一个八连块。例如,下图中有两个八连块。
输入描述:
输入文件中包含多个测试数据,每个测试数据描述了一个网格。每个网格数据的第1行为两个整数:m、n,分别表示网格的行和列;如果m=0,则表示输入结束,否则1<=m<=100,1<=n<=100。接下来有m行数据,每行数据有n个字符(不包括行结束符)。每个字符代表一个小方块,如果为“*”,则代表没有石油,如果为“@”,则代表有石油,是一个pocket。
输出描述:
对输入文件中的每个网格,输出网格中不同的油田数目。如果两块不同的pocket在水平、垂直或者对角线方向上相邻,则被认为属于同一块油田。每块油田所包含的pocket数目不会超过100。
样例输入:
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
样例输出:
2
本题代码:
/*
输入:
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
输出:
2*/
/*注意要求多组输入*/
#include<iostream>
#include<cstring>//memset在这个头文件里
using namespace std;
#define max 100+5
char a[max][max];//油田
int visited[max][max];
int m,n;
//现在位置x,y
void dfs(int x,int y)
{
visited[x][y]=1;//将现在的位置变为已访问过
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{//遍历移动的八个方向,对每一个方向做一次dfs
int nx=x+i,ny=y+j;//向x方向移动i,向y方向移动j
if(nx>=0&&nx<=m-1&&ny>=0&&ny<=n-1&&visited[nx][ny]==0&&a[nx][ny]=='@')//若下一个位置在园子内并且没被访问过并且是油田
{
dfs(nx,ny);
}
}
}
return;
}
int main()
{
while(cin>>m>>n)
{
int res=0;//进行dfs的次数,即为图的连通分量的个数
if(m==0)
break;
memset(visited,0,sizeof(visited));//将visited数组全初始化为0,表示没访问过;1表示已访问过
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)//从有油田@开始dfs
{
if(visited[i][j]==0&&a[i][j]=='@')
{
dfs(i,j);
res++;
}
}
}
cout<<res<<endl;
}
return 0;
}
另一种DFS模板套路:
void dfs(int step)
{
if(边界成立)
{
。。。。(相关操作)
return;
}
for(尝试每一种可能)
{
把这种可能标记表示走过
。。。。(相关操作)
继续下一步dfs(step+1);
把这种可能标记去除
}
return;
}
本题另一种代码:
#include<iostream>
#include<cstring>
using namespace std;
#define max 1000+5
int m,n;
int visited[max][max];
char a[max][max];
void dfs(int x,int y)
{
if(x<0||x>=m||y<0||y>=n||visited[x][y]==1||a[x][y]!='@')
return;
visited[x][y]=1;
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{
int nx=x+i,ny=y+j;
dfs(nx,ny);
}
}
}
int main()
{
while(cin>>m>>n)
{
if(m==0)
break;
int res=0;
memset(visited,0,sizeof(visited));//将visited数组全初始化为0,表示没访问过;1表示已访问过.一定不要忘记初始化!!!因为是全局变量,每一次一定要把visited数组都初始化为0且a数组以及m,n重新输入的
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(visited[i][j]==0&&a[i][j]=='@')
{
dfs(i,j);
res++;
}
}
}
cout<<res<<endl;
}
return 0;
}
例题2.数房子问题
问题描述

代码如下:
/*
样例输入:
5 5
0 0 0 0 0
0 1 1 0 0
0 1 0 0 1
0 1 0 1 0
0 0 0 1 0
样例输出:
3
*/
#include<iostream>
using namespace std;
#define max 105
int a[max][max];
int visited[max][max];
int m,n;
int der[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y)
{
visited[x][y]=1;
for(int i=0;i<4;i++)
{
int nx=x+der[i][0];
int ny=y+der[i][1];
if(nx>=0&&nx<=m-1&&ny>=0&&ny<=n-1&&visited[nx][ny]==0&&a[nx][ny]==1)
{
dfs(nx,ny);
}
}
return;
}
int main()
{
cin>>m>>n;
int res=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(visited[i][j]==0&&a[i][j]==1)//为1表示是房子
{
dfs(i,j);
res++;
}
}
}
cout<<res<<endl;
return 0;
}
另一种方法(emmm其实两种方法是一样的啦)
/*
样例输入:
5 5
0 0 0 0 0
0 1 1 0 0
0 1 0 0 1
0 1 0 0 1
0 1 1 1 1
样例输出:
1
*/
#include<iostream>
using namespace std;
#define max 105
int a[max][max];
int visited[max][max];
int m,n;
int der[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y)
{
if(x<0||x>=m||y<0||y>=n||visited[x][y]==1||a[x][y]==0)
return;
visited[x][y]=1;
for(int i=0;i<4;i++)
{
int nx=x+der[i][0];
int ny=y+der[i][1];
dfs(nx,ny);
}
}
int main()
{
cin>>m>>n;
int res=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(visited[i][j]==0&&a[i][j]==1)//为1表示是房子
{
dfs(i,j);
res++;
}
}
}
cout<<res<<endl;
return 0;
}
全球变暖——第九届蓝桥杯第9题
标题:全球变暖
你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
注意是有多少会被完全淹没,不是剩下了多少!!!且要注意若淹没后陆地更凌乱了,可能会岛屿数反而会加多,这时完全淹没的岛屿数应为0。
代码中我输出了中间过程,最后一行的数字是要求的结果。
/*
输入:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出:
1
*/
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 100
int n;
int ans;
int re_ans;
char a[maxn][maxn];
int visited[maxn][maxn];
int dir[4][2]={-1,0,1,0,0,-1,0,1};
void dfs(int x,int y)
{
for(int i=0;i<4;i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx>=0&&nx<n&&ny>=0&&ny<n&&visited[nx][ny]==0&&a[nx][ny]=='#')
{
visited[nx][ny]=1;
dfs(nx,ny);
}
}
return;
}
bool panduan(int i,int j)
{
if(a[i-1][j]=='.'||a[i+1][j]=='.'||a[i][j-1]=='.'||a[i][j+1]=='.')
return true;
else
return false;
}
void yanmo()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(a[i][j]=='#'&&panduan(i,j))
{
a[i][j]='a';
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(a[i][j]=='a')
{
a[i][j]='.';
}
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
}
}
ans=0;
memset(visited,0,sizeof(visited));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(a[i][j]=='#'&&visited[i][j]==0)
{
visited[i][j]=1;
dfs(i,j);
ans++;
}
}
}
cout<<"淹没前有"<<ans<<endl;
yanmo();
cout<<endl;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cout<<a[i][j];
}
cout<<endl;
}
re_ans=0;
memset(visited,0,sizeof(visited));//一定要注意重新清0!!
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(a[i][j]=='#'&&visited[i][j]==0)
{
visited[i][j]=1;
dfs(i,j);
re_ans++;
}
}
}
cout<<"淹没后有"<<re_ans<<endl;
if(re_ans<ans)
cout<<ans-re_ans<<endl;
else
cout<<0;
return 0;
}