首先,搜索的意思指仔细查找、搜寻。
那么深度搜索,就是一直往深处查找,搜寻,寻找答案,如果搜到头了都没有找到,那就换一条路继续一往无前的搜索,直到找到答案,或是把所有能搜的地方都搜过为止。
下面以一个例题为引子来介绍一下深度搜索(来自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(下左),就继续深度搜索,知道把所有船只的所有方向全部看完为止。