第一周算法训练(dfs)(深度优先搜索算法)

本文介绍了深度优先搜索(DFS)算法的概念、基于递归和回溯的工作原理,列举了DFS在解决组合、切割等问题中的应用,并提供了C语言的示例代码,包括两道洛谷题目。
摘要由CSDN通过智能技术生成

1.什么是dfs,以及算法的基础是什么?

dfs:深度优先搜索算法,是一种用于遍历或搜索树或图的算法.沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。简单来说就是一条路走到黑,直到没路了,或者找到结果才返回。

深度优先搜索算法的基础:递归与回溯

递归与回溯是相辅相成的,有递归就会有回溯,递归函数的下面部分就是回溯的过程以及回溯的逻辑

回溯是一种纯暴力搜索。

2.使用原因

虽然深度优先搜索算法的效率很低,但是有些问题只能通过dfs来解决,有些时候多重循环就照不出来结果

能够解决的问题有:

1.组合问题         

2.切割问题

3.子集问题

4.排列问题

5.棋盘问题

3.如何去理解深度优先搜索的回溯算法

回溯算法是一个很抽象的东西,但是所有的回溯算法都可以抽象成一个树状结构,可以将其抽象成一个n叉树问题。树的深度取决于要搜索问题的层数,树的宽度取决于每个节点处理集合的大小。

回溯法的模版:

void 函数名(参数)//回溯算法的参数一般比较多,要根据具体情况进行分析
{
    if(终止条件)
      {
         收集最后节点结果
         return  ;
      }
//单层搜索的逻辑:
    for(集合的元素)//遍历的是集合里的每一个元素,也可能是集合节点的子节点个数
      {
          处理节点
          递归函数
          回溯操作(撤销处理节点的操作)
      }
    return ;
}

基本上一般的回溯问题都可以通过这个模版的变形从而解决问题

4.例题以及AC代码(C语言)

1.第一题

这是洛谷上的一道dfs的入门题,我们通过这题来了解一下dfs

#include <stdio.h>
int n,m,t,p,q;//n和m分别代表迷宫有多少行和列,t代表有多少个障碍物,p和q是障碍物的坐标
int sx,sy,fx,fy;//输入起始位置和终止位置坐标
int a[6][6];//棋盘数组
int b[6][6];//标记数组,若元素是1,则代表这个位置已经访问过了,不用再次访问
int next[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//要进行操作的集合,分别代表右,下,左,上的移动
int count=0;//统计共有多少种方法
void dfs(int x,int y)//进行回溯的函数
{
    int dx,dy;//代替x,y进行移动操作,这样回溯的时候不用回溯x和y,只需要回溯标记就好,更简洁
    if(x==fx&&y==fy)//终止条件,当x和y到达终止位置时,会将次数加一,并且终止递归
    {
        count++;
        return ;
    }
    for(int i=0;i<4;i++)//遍历移动集合,进行移动
    {
        dx=x+next[i][0];//移动后的行坐标
        dy=y+next[i][1];//移动后的列坐标
        if(dx<1||dx>n||dy<1||dy>m)//假如数组越界就可以直接跳过(即边界的判断条件)
        {
            continue;
        }
        if(b[dx][dy]==0&&a[dx][dy]==0)//这个位置没有被访问过
        {
            b[dx][dy]=1;//标记访问
            dfs(dx,dy);//继续递归
            b[dx][dy]=0;//回溯
        }
    }
    return ;
}
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    scanf("%d%d%d%d",&sx,&sy,&fx,&fy);
    b[sx][sy]=1;//将其实位置标记为1,代表一开始就访问过了
    while(t--)
    {
        scanf("%d%d",&p,&q);
        b[p][q]=1;//将障碍物标记为1,相当于访问过这样一视同仁代码更加简洁
    }
    dfs(sx,sy);//调用递归函数
    printf("%d\n",count);
}

可以看到这个基础题的代码和上述模版十分相似,但是后续的难题就不能完全依靠模板了,那个只是起到一个辅助的思路,主要还是需要自己思考

2.第二题

首先根据题意,我们可以知道只有附近八格内,只要连续就可以构成一个水坑,那么我们就可以想到终止条件就是假如附近八格内没有水了就可以直接结束了,而函数里的参数就是首个出现的水坑,我们要进行循环的集合就是水坑的附近八格,因此我们可以写出AC代码

#include <stdio.h>
int n,m;
char a[105][105];//定义一个棋盘数组
int b[105][105];//标记数组
int count=0;
void dfs(int x,int y)//递归函数
{
    if(a[x][y]=='.')//终止条件就是碰到  .
    { 
        return ;
    }
    int dx,dy;
    for(int i=-1; i<=1; i++)
    {
        for(int j=-1; j<=1; j++)
        {
            dx=x+i;
            dy=y+j;
            if(dx>=0&&dx<=n&&dy>=0&&dy<m&&a[dx][dy]=='W'&&b[dx][dy]==0)
            {
                b[dx][dy]=1;
                dfs(dx,dy);//此处无需回溯,因为需要进行标记
            }
        }
    }
    return ;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
    {
        scanf("%s",a[i]);
    }
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<m; j++)
        {
            if(a[i][j]=='W'&&b[i][j]==0)
            {
                dfs(i,j);
                count+=1;
            }
        }
    }
    printf("%d\n",count);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值