dfs求联通块(油田,01迷宫)

1.先用一个经典的问题来说下联通块
油田”问题是一个比较经典的体现DFS思想的题目,经过学习,对DFS也有了一点理解,下面介绍下这个题目~
题目来源:

Mid-Central USA 1997,ZOJ1709,POJ1562

题目描述:

GeoSurvComp地质探测公司负责探测地下油田。每次GeoSurvComp公司都是在一块长方形的土地上来探测油田。在探测时,他们把这块土地用网格分成若干个小块,然后逐个分析每块土地,用探测设备探测地下是否有油田。土地底下有油田则成为pocket,如果两个pocket相邻,则认为是同一块油田,油田可能覆蓋多个pocket。试计算长方形的土地上有多少个不同的油田。

输入描述:

输入文件中包含多个测试数据,每个测试数据描述了一个网格。每个网格数据的第1行为两个整数:m、n,分别表示网格的行和列;如果m=0,则表示输入结束,否则1<=m<=100,1<=n<=100。接下来有m行数据,每行数据有n个字符(不包括行结束符)。每个字符代表一个小方块,如果为“*”,则代表没有石油,如果为“@”,则代表有石油,是一个pocket。

输出描述:

对输入文件中的每个网格,输出网格中不同的油田数目。如果两块不同的pocket在水平、垂直或者对角线方向上相邻,则被认为属于同一块油田。每块油田所包含的pocket数目不会超过100。

样例输入:

5 5

****@

@@@

*@**@

@@@*@

@@**@

样例输出:

2

可以看到只要是连在一起的@就算作一个联通快。图中map[1][5],map[2][5],map[3][5],map[4][5],map[5][5]是联通块1号,其余@是联通块2号。下面给出思路:遍历每一个点,如果访问过或者不是@我们就不遍历,以这个点为起点开始搜,搜到@就记录一下。下面给出代码。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=100+5;
char pic[maxn][maxn];
int n,m,vis[maxn][maxn];
void dfs(int r,int c,int id)
{if(r<0||r>=m||c<0||c>=n)return;//判断边界
if(vis[r][c]>0||pic[r][c]!='@')return;//判断是不是遍历过,和是不是@
vis[r][c]=id;//标记此点已被遍历
for(int i=-1;i<=1;i++)//八方搜索
for(int j=-1;j<=1;j++)
if(i!=0||j!=0)
dfs(i+r,j+c,id);
}
int main()
{while(cin>>m>>n)
{for(int i=0;i<m;i++)
cin>>pic[i];
memset(vis,0,sizeof(vis));
int cnt=0;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(vis[i][j]==0&&pic[i][j]=='@')
dfs(i,j,++cnt);
cout<<cnt<<endl;
}
return 0;
}

2.来自洛谷的一道题,稍微有一点变化
01迷宫
链接:
https://www.luogu.org/problemnew/show/P1141
题目描述

有一个仅由数字00与11组成的n \times nn×n格迷宫。若你位于一格0上,那么你可以移动到相邻44格中的某一格11上,同样若你位于一格1上,那么你可以移动到相邻44格中的某一格00上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入输出格式

输入格式:
第11行为两个正整数n,mn,m。

下面nn行,每行nn个字符,字符只可能是00或者11,字符之间没有空格。

接下来mm行,每行22个用空格分隔的正整数i,ji,j,对应了迷宫中第ii行第jj列的一个格子,询问从这一格开始能移动到多少格。

输出格式:
mm行,对于每个询问输出相应答案。

输入输出样例

输入样例#1: 复制
2 2
01
10
1 1
2 2
输出样例#1: 复制
4
4
说明

所有格子互相可达。

对于20%20%的数据,n≤10n≤10;

对于40%40%的数据,n≤50n≤50;

对于50%50%的数据,m≤5m≤5;

对于60%60%的数据,n≤100,m≤100n≤100,m≤100;

对于100%100%的数据,n≤1000,m≤100000n≤1000,m≤100000。

思路:这题我们也用联通块的思想只不过,搜的时候每次变化一下,比如上次是1这次就是0.
代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,ans[100002],x,y,f[1002][1002];
char s[1002][1002];
void dfs(int r,int c,int z,int lll)
{if(r<0 || r>=n || c<0 || c>=n ||f[r][c]!=-1||s[r][c]-'0'!=z)
return;
ans[lll]++;//ans数组记录这次遍历的大小
f[r][c]=lll;
dfs(r-1,c,!z,lll);dfs(r+1,c,!z,lll);dfs(r,c-1,!z,lll);dfs(r,c+1,!z,lll);//这一步是关键,我们用!z来变换我们要找的目标,其他跟上题没什么区别
}
int main()
{cin>>n>>m;
for(int i=0;i<n;i++)
cin>>s[i];
 memset(f,-1,sizeof(f));
   for (int i=0;i<=m;i++)
    {
        cin>>x>>y;
        x--;
        y--;
        if (f[x][y]==-1)dfs(x,y,s[x][y]-'0',i);else ans[i]=ans[f[x][y]];
    }
    for(int i=0;i<m;i++)
    cout<<ans[i]<<endl;
}
点赞