连通块问题可以说是整个搜索算法中比较经典的一类问题了。作为一个经典题型,它的思路是非常简单的。简单来说就是,把dfs当做一个标记地盘的工具。所谓地盘, 指的是我们要找的连通块占的位置。
伪代码(基本思路):
dfs(){
搜索上下左右四个位置是否符合条件。
if(符合条件) {
标记
dfs()
}
}
这里会用到一个熟悉搜索的朋友们经常使用的数组(数组名因人而异),按照我的习惯应该开成:
nx[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
这个数组是用来干什么的呢?简单来说,就是叠加到dfs函数传过来的坐标 x 和 y 上,相当于遍历上下左右四个位置,然后就可以 ' 为所欲为 ' 了。/斜眼笑
而相较之下,连通块的思路有一点细微的差别,那就是在主函数main()中进行dfs()遍历的时候,需要用一个计数器(ans, cnt, sum之类的)记录下主函数中调用dfs()函数的次数,放到题目当中也就相当于是连通块的数目,所以最后把这个计数器的值输出就好了。
那么,接下来我们来看一道例题:
踏青
Description
小白和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由一块一块格子组成的,有的格子上是草丛,有的是空地。草丛通过上下左右 4 个方向扩展其他草丛形成一片草地,任何一片草地中的格子都是草丛,并且所有格子之间都能通过上下左右连通。如果用'#'代表草丛,'.'代表空地,下面的峡谷中有 2 片草地。
##..
..##
处在同一个草地的 2 个人可以相互看到,空地看不到草地里面的人。他们发现有一个朋友不见了,现在需要分头去找,每个人负责一片草地,想知道他们至少需要多少人。
Input
第一行输入 n, m (1 ≤ n,m ≤ 100) 表示峡谷大小。
接下来输入 n 行字符串表示峡谷的地形。
Output
输出至少需要多少人。
Sample Input 1
5 6
.#....
..#...
..#..#
...##.
.#....
Sample Output 1
5
题目分析
这道题乍一看有些乱七八糟的,但仔细想想就会发现其实它是一道 纯纯的 连通块问题 不然也不会出现在这儿了。思路参上。
AC代码
#include
using namespace std;
int n, m;
int ans;
char grass[110][110];
int vis[110][110];
int nx[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
bool in(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= m;
}
void dfs(int x, int y) {
for (int i = 0; i <= 3; i++) {
int nowx = x + nx[i][0];
int nowy = y + nx[i][1];
if (in(nowx, nowy) && grass[nowx][nowy] == '#' && !vis[nowx][nowy]) {
vis[nowx][nowy] = 1;
dfs(nowx, nowy);
}
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> grass[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (grass[i][j] == '#' && !vis[i][j]) {
ans++;
vis[i][j] = 1;
dfs(i, j);
}
}
}
cout << ans;
return 0;
}
感谢阅读~~