深度优先搜索——DFS遍历

深度优先搜索是一个递归过程,有回退过程。每个顶点的访问次序称为顶点的深度优先数。访问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的回退过程
    }
}

 

 

 

 

 

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值