CSDN-c++算法如此简单(深度优先搜索-dfs算法)

一、深度优先搜索的定义

在前面我们学习递归的时候提到递归树,用递归的手段可以遍历到这个树形结构。其实对于任意一张有联通关系的图,我们同样可以使用递归的手段进行遍历。
其中以深度为第一优先级进行遍历的方式我们称为:深度优先搜索(dfs),即沿着一条路一直走,直到走不通。

像这样一张图,存在一个环。
通常情况下,如果对于某点我们只需要遍历一次的话,那我们要对走过的点进行标记,否则会形成死循环。(一直在环内部搜索)
所以在dfs时候,需要进行标记。即未遍历过的点标记为0,遍历过的点标记为1,标记为1的点则不再进行遍历。
本图的正常枚举顺序为:1 - 2 - 5 - 9 - 10 - 6 - 3 - 7- 4 - 8

二、dfs的连通性处理

判断点x和点y是否连通,即从点x开始进行搜索遍历,如果按照规则能正常遍历到y,则说明连通。

三、dfs的连通计数问题

使用dfs可以访问从起点出发的且符合条件的所有点,因此一次dfs能够把所有符合条件的点全部标记出来,此时如果在dfs中加一个计数,就可以很轻松得到本次dfs访问过的点的数量。简单来说就是,把dfs当做一个标记地盘的工具。连通块大小,就是我们占的地盘大小。

四、dfs的连通块计数

我们刚才提到一次dfs可以枚举到从当前起点出发的符合条件的所有点,即可以将所有与之直接或间接连通的点找到。我们把这样的点集可以称作一个连通块。
当然一个图上连通块的数目不一定唯一。如下图所示,一共有5个连通块。

如何进行连通块数目统计?
一次dfs可以标记一个连通块,因此我们在一张图中找到符合条件的起点开始dfs,连通块的数量则等价于dfs执行的次数。


以图为例:
逐行逐列进行枚举,当找到值为0的点,进行一次dfs,并把枚举到的所有点值修改为1,这样我们甚至不用标记是否访问。


显然,一共需要进行5次dfs

五、题目练习+讲解

1.题目

有一个仅由数字 0 与 1 组成的 n×n 格迷宫。若你位于一格 0 上,那么你可以移动到相邻 4 格中的某一格 1 上,同样若你位于一格 1 上,那么你可以移动到相邻 4 格中的某一格 0 上。你的任务是:给定m个询问,求出从某一格开始能移动到多少个格子(包含自身)。(n<=1000,m<=10000)

2.题解

#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[1005][1005];
int vis[1005][1005];
int dx[5]={0,-1,0,1,0};
int dy[5]={0,0,1,0,-1};
int id,cnt;
int p[1005][1005];
int size[1000005];
void dfs(int x,int y){
	p[x][y]=id;
	cnt++;
	for(int k=1;k<=4;k++){
		int i=x+dx[k];
		int j=y+dy[k];
		if(i<1||i>n||j<1||j>n){
			continue;
		}
		if(a[i][j]==a[x][y]){
			continue;
		}
		if(vis[i][j]){
			continue;
		}
		vis[i][j]=1;
		dfs(i,j);
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(vis[i][j]==0){
				vis[i][j]=1;
				cnt=0;
				id++;
				dfs(i,j);
				size[id]=cnt;
			}
		}
	}
	while(m--){
		int x,y;
		cin>>x>>y;
		cout<<size[p[x][y]]<<endl;
	}
	return 0;
}

3.讲解

1.连通块统计
每次dfs即表示新建建一个连通块。一定要将信息记录完整。包含点与连通块的关系,连通块大小。


2.判断条件
能够进入下个点的关键,有的时候可能描述更加复杂,但要总结当前点与下一个点的关系。


3.vis[i][j]=1
有些题目我们没有标记起点,因为最后会遍历到,但是这个细节大家要注意。第一次进入dfs时就已经进行统计,因此最后不能再重复统计。

深度优先搜索-dfs算法到这里就结束了。

感谢各位大佬的拜读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值