【专题】深度优先搜索(DFS) 从入门到精通—— 结合洛谷习题

16 篇文章 3 订阅
2 篇文章 0 订阅

深度优先搜索(DFS) 结合洛谷习题


前言

当处于一个巨大的迷宫中,你只有第一人称视角,应该如何以最快速度走出这个迷宫呢?

依据深度优先算法,这种策略一定有效:以当前位置为起点,沿着一条路向前走。当遇到一个岔路口时,就选择一个岔路前进,如果这个岔路是死胡同,就退回上一个岔路口;如果这个岔路又有新的岔路口,就依据之前的做法,再选择一条岔路口继续前进。

这样就出现了一个问题,当经历了众多岔路口之后,你的面前出现了死胡同,你的记忆力已经不支持你退回一开始的岔路口,那岂不是又要在迷宫里打转转了?其实解决这个问题的方法很简单,无论是选择岔路口或者是返回上一个岔路口,只要保证在走的过程中你的右手一直贴着右边的墙壁,你就一定会走出迷宫。

这种不撞南墙不回头的策略,形象地从深度上彻底挖掘了这个迷宫,因此这种搜索路径的方法被称作深度优先搜索,也称为DFS(Depth-First-Search)

一、深度优先搜索是什么?

在这里插入图片描述 采用递归的方法,先沿一条路搜到底,在递归回上一个节点,沿另一个方向搜索,以此类推。这就是广义上的深搜。
正如图上所示,运用深搜的思路,我们会沿着数字的顺序进行搜索,即数字的顺序就是搜索的路径。
首先,我们会从起点->1->2->3,此时到3已经无路可走,我们就要退回删一个节点2->4,此时4已经无路可走,退回5->6,退回5->7以此类推。
最终,我们就遍历了一次所有的节点,于此同时,也找到了通往各节点的路径。

到此可能你对深搜还是一知半解,没关系,跟随着下面的例题
一起进入深搜的应用,加深对深搜的理解!

二、洛谷例题

1.P1706全排列问题

题目描述

输出自然数 1到 n所有不重复的排列,即 n*n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式

一个整数 n

输出格式

由 1∼n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 55 个场宽。

输入输出样例

输入 #1

3

输出 #1

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

题解:

思路:

以n=3举例:

首先,分别建立两个数组,一个int型的数组a,用于记录每种情况,一个bool型数组vis,用于判断当前数字是否使用过。

之后,编写dfs函数:先判断当前的vis[i]是否为0,如果为0则说明未被访问过,则进入递归程序。(具体实现在代码解读)

最后,如果当前搜索层数大于n时,退出递归。
#include<iostream>
#include<iomanip>
using namespace std;
int a[20];
bool vis[20];
int n;
void print()
{
    for(int i=1;i<=n;i++)
    {
        cout<<setw(5)<<a[i];//setw()指定宽度
    }
    cout<<endl;
}
void dfs(int x)
{
    if(x>n) print();
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            a[x]=i;
            dfs(x+1);
            vis[i]=0;
        }
    }
}
int main()
{
    cin>>n;
    dfs(1);
}

代码解读:

 //递归(dfs)主体
 if(!vis[i])
        {
            vis[i]=1;//未访问过的现在要标记已访问
            a[x]=i;//注意是a[x],x代表当前搜索的层数,在当前这一层选一个数i
            dfs(x+1);//继续往下一层搜索
            vis[i]=0;//搜索完之后,要释放上一层的数,以便下一次使用
        }

2.P1605 迷宫

题目背景

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

题目描述

输入格式

第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式

给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入输出样例

输入 #1

2 2 1
1 1 2 2
1 2

输出 #1复制

1

说明/提示

【数据规模】

1≤N,M≤5

题解

#include<cstdio>
#include<iostream>
using namespace std;
int n,m,t,sx,sy,fx,fy,ans;
bool vis[10][10];
bool mp[10][10];
int Xx[]={1,0,-1,0};
int Yy[]={0,-1,0,1};
void dfs(int x,int y)
{
    if(x==fx&&y==fy)
    {
        ans++;
        return;
    }
    for(int i=0;i<4;i++)
    {
        int dx=Xx[i]+x;
        int dy=Yy[i]+y;
        if(dx>=1&&dx<=n&&dy>=1&&dy<=m&&!vis[dx][dy]&&!mp[dx][dy])
        {
            vis[x][y]=1;
            dfs(dx, dy);
            vis[x][y]=0;
        }
    }
}
int main()
{
    cin>>n>>m>>t;
    cin>>sx>>sy>>fx>>fy;
    while(t--)
    {
        int x,y;
        cin>>x>>y;
        mp[x][y]=1;
    }
    dfs(sx,sy);
    cout<<ans;
    return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值