基础算法:深度搜索(DFS)

首先,搜索的意思指仔细查找、搜寻。

那么深度搜索,就是一直往深处查找,搜寻,寻找答案,如果搜到头了都没有找到,那就换一条路继续一往无前的搜索,直到找到答案,或是把所有能搜的地方都搜过为止。

下面以一个例题为引子来介绍一下深度搜索(来自luogu P1596,USACO10OCT):

相传那年群雄四起,众多势力争相抢夺霸主地位,但是谁都不服谁,但是论步兵实力的话大家都不分上下,于是,众人便决定进行海战对决,通过海战决出霸主的地位。

而你非常幸运的获得了一张观场卷,于是当天,你来到了现场:

W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

(.是海,w是船)

面对一望无垠的大海和长得都差不多的船只,你有点懵了。

“不是,这船只都长得差不多,这能看出哪些船只是一队的?”(你的内心独白)

旁边一个高大男子似乎看穿了你的想法,笑着有礼貌的解释道:

“这你都看不出来?很明显啊,每个队的船只都紧挨在一起,和其他队保持距离呢。”

于是你仔细一看,发现确实如此。

你发现,只要船只的周围(周围的8格)有其他船只,那么这两个船只就是一个队的。

于是,你很快看出了这片海域有几个队在参加争霸。

那么,这个问题如何用dfs来实现呢?

首先,想要求有几个队,首先我们需要先将这片海域的信息告诉计算机( 这是一片n*m的海域):

char mp[110][110];

cin>>n>>m;
for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	cin>>mp[i][j];

有了这片海域的信息,我们就可以开始寻找船只了。

为了防止漏掉船只,我们从这片海域的最左上角开始找,很快,我们找到了第一个船只:

(1,1);

找到船只以后,我们就可以开始对船只进行搜索,看看哪些船只和这个船是一个队的。

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
	if(mp[i][j]=='W')//如果这块海域上是船只
{
	dfs(i,j);sum++;//开始找和这个船是一队的,并且队数加一
}
	}

那么,怎么实现搜索呢?

若果是我们来观察的话,我们会先看第一个船周围有没有船只,如果有那么再看船只周围的船只是否有船只,直到新的周围船只周围不再有新船只为止。

那么,我们是否可以按这个思路来用计算机实现呢?

首先,我们已经看到了第一个船只,那么,我们现在要的就是看船只的周围有没有船只,那么,如何实现看船只的周围呢?

int dx[9]={0,1,-1,0,0,1,-1,1,-1};
int dy[9]={0,0,0,1,-1,1,-1,-1,1};

for(int i=1;i<=8;i++)
	{
		mp[x+dx[i]][y+dy[i]];
	}

是的,我们可以用两个数组来存上、下、左、右、上左、上右、下左、下右。

然后通过一个循环来巡视8个方向。

那么,根据深度搜索的定义,即一往无前的搜索,那么,如果我们发现新的船只,我们就看一下新的船只周围是否有船只。

for(int i=1;i<=8;i++)
	{
		if(mp[x+dx[i]][y+dy[i]]=='W')dfs(x+dx[i],y+dy[i]);
	}

如果新的船只周围有船只的话(为'w‘’),那么我们就继续搜索新的船只周围有没有新的船只(dfs(x+dx[i],y+dy[i]))

这个时候,我们观察我们已经写了的,发现只要周围有船只,我们就会跳到那个船只上搜索。

那这样,我们不就会在两个船只直接来回跳来跳去吗?

为了解决这一问题,每次搜索前,我们都可以假装之前搜过的船只都不存在了,反正虽然船只不存在了,但我们还是到时候会回来把它的四周巡视完的。

于是我们在循环前面加上这一句:

mp[x][y]='.';

于是,这个问题的代码就出来了:

#include<iostream>
using namespace std;

char mp[110][110];
int dx[9]={0,1,-1,0,0,1,-1,1,-1};
int dy[9]={0,0,0,1,-1,1,-1,-1,1};
int n,m,sum=0;

void dfs(int x,int y)
{
	mp[x][y]='.';
	for(int i=1;i<=8;i++)
	{
		if(mp[x+dx[i]][y+dy[i]]=='W')dfs(x+dx[i],y+dy[i]);
	}
	
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	cin>>mp[i][j];
	
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
	if(mp[i][j]=='W'){
	dfs(i,j);sum++;}
	}
	cout<<sum;
	return 0;
}

那么,我们不难发现,深度搜索的思路就是从头到尾搜索,然后根据所给条件来进行深度搜索,知道找到答案位置。

上面给的例子是简单的搜索例题,那么,常见的搜索题一般包含四个步骤:

void dfs(int x){

//1:判断是否已经满足找到答案或已经搜索到头了,然后返回,不然有的问题没有特殊限制,搜索可能会无限搜索,比如+2判断一个答案,没有返回的话就会无限一直加2

循环{
//2:判断是否能进行深度搜索,如果能,就进入深度搜索,并且标记这个点为已读,防止在下一个点和这个点之间来回搜索

//3.进入深度搜索dfs(下一个值)

//4.已经搜索完成,解除标记,防止下一条路径用到这个点。
}
}

 这是海域问题的示意图:

1-8为w(船只)的八个方向,3(左边)有船只后便开始搜索下一个船只的八个方向,6(上右)有船只后开始搜索下一个船只的方向,如果搜索完了就返回到上一个船只搜索的地方6(上右),然后继续把没看完的地方看完,看完之后继续返回当前船只的上一个船只把没看完的看完,如果有新船只7(下左),就继续深度搜索,知道把所有船只的所有方向全部看完为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值