连通块问题(DFS)

连通块问题可以说是整个搜索算法中比较经典的一类问题了。作为一个经典题型,它的思路是非常简单的。简单来说就是,把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;
}

感谢阅读~~

你可能感兴趣的:(搜索,搜索)