深度优先搜索是一个递归过程,有回退过程。每个顶点的访问次序称为顶点的深度优先数。访问n个顶点时经过的n-1条边,这n-1条边将n个顶点连接成一棵树,称此图为原图的深度优先生成树,该树的根结点是深度优先搜索的起始顶点。
例题1:骨头的诱惑
题目描述:迷宫是一个N*M的长方形,迷宫有一个门,刚开始门是关着的,这个门会在第T秒钟开启,门只会开启很短的时间(少于一秒),因此小狗必须恰好在第T秒到达门的位置。每秒钟,它可以移动一个方格,但它一旦移动到相邻的方格,这个方格开始下沉,而且会在下一秒消失,所以它不能在一个方格中停留超过一秒,也不能回到经过的方格。
输入数据:X:墙壁,不能进入 S:小狗所处的位置 D:迷宫的门 . :空的方格
输出:YES 或NO
分析:搜索时从小狗所在的初始位置S出发进行搜索。没搜索到一个方格位置,对该位置的四个可能方向(排除边界和墙壁)进行下一步搜索。往前走一步,要将到达的位置设置成墙壁,表示当前搜索过程不能回到经过的方格,一旦前进不了,要回退,要恢复现场(将前面设置成的墙壁还原成空的方格),回到上一步的情形。只要有一个搜索分支达到门的位置且符合要求,则搜索过程结束。
代码如下
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,t;
int di,dj;
bool escape;
char map[9][9];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};///表示上,下,左,右四个方向
void dfs(int si,int sj,int cnt);
int main()
{
int i,j;
int si,sj;
while(scanf("%d%d%d",&n,&m,&t)!=EOF)
{
if(n==0&&m==0&&t==0) break;
char temp;
int wall=0;///记录墙的数目
scanf("%c",&temp);/// 用于接收换行符
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%c",&map[i][j]);
if(map[i][j]=='S')
{
si=i;
sj=j;
}
else if(map[i][j]=='D')
{
di=i;
dj=j;
}
else if(map[i][j]=='X')
wall++;
}
scanf("%c",&temp);///用于接收换行符
}
if(n*m-wall<=t)///搜索前的剪枝
{
printf("No\n");
continue;
}
escape=0;
map[si][sj]='X';
dfs(si,sj,0);
if(escape)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
void dfs(int si,int sj,int cnt)
{
int i,temp;
if(si>n||sj>m||si<=0||sj<=0) return ;
if(si==di&&sj==dj||cnt==t)
{
escape=1;
return ;
}
temp=t-cnt-fabs(si-di)-fabs(sj-dj);///搜索过程中的剪枝
if(temp<0||temp%2) return ;
for(i=0;i<4;i++)
{
if(map[si+dir[i][0]][sj+dir[i][1]]!='X')
{
map[si+dir[i][0]][sj+dir[i][1]]='X';
dfs(si+dir[i][0],sj+dir[i][1],cnt+1);
if(escape) return ;
map[si+dir[i][0]][sj+dir[i][1]]='.';
}
}
return ;
}
例题2(oil deposits)(详情网上有)
代码如下(最简单的深搜)
#include<cstdio>
#include<iostream>
using namespace std;
#define MAXN 110
char Edge[MAXN][MAXN];
int dir[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1} };///方向分别表示左上;上;右上;右;右下;下;左下;左八个方向.从一个点向周围发射箭头,和轴垂直的为0;越靠近原点的数越小;
int m,n; ///只要和图有关的,一般都要在文件头定义两条边。 ///往x箭头方向为正,y箭头方向为正;反之为负
void dfs(int x,int y)
{
int i,j;///循环必须定义的变量,一般在函数头部或者整个文件的头部定义。
int xx,yy;
Edge[x][y]='*';
for(i=0;i<8;i++)
{
xx=x+dir[i][0];
yy=y+dir[i][1];
if(xx<0||xx>=m||yy<0||yy>=n) continue;
if(Edge[xx][yy]=='@') dfs(xx,yy);
}
}
int main()
{
int i,j;
int Count;
while(scanf("%d%d",&m,&n)!=EOF)
{
if(n==0||m==0) break;
for(i=0;i<m;i++) scanf("%s",Edge[i]);
Count=0;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
if(Edge[i][j]=='@')
{
dfs(i,j);
Count++;
}
}
}
cout<<Count<<endl;
}
return 0;
}
dfs的伪代码:
(1)用邻接表存储图
void dfs(顶点i)
{
visited[i]=1;
p=顶点i的边链表表头指针;
while(p不为空)
{
if(顶点j未访问过)
{
dfs(顶点j);
///以下是dfs的回退过程,在很多应用中需要在这里写代码
}
p=p->next;
}
}
(2)用邻接矩阵存储图
void dfs(顶点i)
{
visited[i]=1;
for(j=0;j<n;j++)
{
///准备工作
if(map[i][j]==1&&visited[j]==0)///j是i的邻接顶点,且顶点j未被访问过
dfs(j);
///以下为dfs的回退过程
}
}