<<算法很美>>——(七)——DFS典题(一):水洼数目

目录

问题描述

问题分析

放码过来


问题描述

有一个大小为N×MN×M N\times MN×M的园子,雨后积起了水。八连通的积水被认为是连接在一起的。请求出园子里总共有多少水洼?八连通指的是下图中相对W的∗部分,‘W’表示积水,’ * '表示没有积水。

∗∗∗
∗W∗
∗∗∗

输入样例

10 12
W********WW*
*WWW*****WWW
****WW***WW*
*********WW*
*********W**
**W******W**
*W*W*****WW*
W*W*W*****W*
*W*W******W*
**W*******W*

输出样例

3

问题分析

假如这个红色的地方有水,首先以它为中心,进行找,找到了就以新的为中心继续往下找

但这样会出现一个问题,就是它会往回走,这样就会陷入死循环,怎么样才能避免这样的事情发生囊?大家先思考下,这里留个悬念下面讲

继续往前走,发现前面没有积水了,它开始找另外方向有没有积水,发现没有后,就会退到上一层,继续寻找其他方向有没有积水,没有继续往回退,直到退到第一个积水的八个方向都走完,这样就完成了。

ok,我们开始思考怎么样才能避免它往回走陷入死循环的情况囊?

只要我们走到的位置有积水,走过后我们就把积水抽掉,让它变的干燥,就是上面例子中给的当走到的地方有积水也就是'W‘走过后把它变成'*’,这样遍历完后他都变成‘*'了也就结束了,避免了死循环.

这道题很经典,这种思想可以说在深搜中屡试不爽,所以我们可以把这个当做例题模板,背下来,遇到类似的题,像迷宫,岛屿之类的可以直接套模板。

话不多说,

放码过来

#include<iostream>
#include<cstring>
using namespace std;
int cnts;
//n是行,m是列
int n, m;
void dfs(string* a, int i, int j)
{
	//a[i][j] = '.';
	//if (i > 0 && a[i - 1][j] == 'W')dfs(a, i - 1, j);//上方
	//if (i <n-1&& a[i + 1][j] == 'W')dfs(a, i + 1, j);//下方
	//if (j > 0 && a[i][j - 1] == 'W')dfs(a, i, j - 1);//左
	//if (j < m - 1 && a[i][j + 1] == 'W')dfs(a, i, j + 1);//右
	//.....
	a[i][j] = '.';
	for (int k = -1; k < 2; k++)//-1,0,1
	{
		for (int l = -1; l < 2; l++)//-1,0,1
		{
			if (k == 0 && l == 0)continue;
			if (i + k >= 0 && i + k <= n - 1 && j + l >= 0 && j + 1 <= m - 1)
			{
				if (a[i + k][j + l] == 'W')
				{
					dfs(a, i + k, j + l);
				}
			}
		}
	}
}
int main()
{
	cin >> n >> m;
	string* a=new string[n];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (a[i][j] == 'W')
			{
				dfs(a,i,j);
				cnts++;
			}
		}
	}
	cout << cnts << endl;
	return 0;
}

我们再回来看下这个题目:

 我们使用双重for循环,来遍历这个二维数组,首先发现第一组的'W’就DFS遍历,遇到'W‘就继续往下走,直到把第一组的所有'W’都变为'.',就返回,这时第一个水洼就遍历完了,cnts对应加1,继续进行for循环找下一组'W‘的位置,如上图的2和3.重复1的操作,直到把所有的'W’遍历为'.‘这样for循环也就结束了.cnts此时的数就是水洼数。

这里我们思考一个问题,这里可以回溯嘛?答案肯定是不可以的,因为当我们遍历并把其改为'.’再进行回溯又变成了'W‘这样来来回回根本统计不完,所以回溯的使用要根据每个题目的不同进行灵活判断。

猜你喜欢

转载自blog.csdn.net/m0_58367586/article/details/123937530