首页 > 编程语言 >深度优先搜索算法-dfs讲解

深度优先搜索算法-dfs讲解

时间:2023-06-19 21:32:04浏览次数:51  
标签:剪枝 return 字符 int 迷宫 dfs 搜索 搜索算法 讲解

迷宫问题

有一个迷宫:

S**.
....
***T

(其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地。你需要从S出发走到T,每次只能向上下左右相邻的位置移动,不能走出地图,也不能穿过墙壁,每个点只能通过一次。)

现在需要你求出是否可以走出这个迷宫

我们将这个走迷宫过程称为dfs(深度优先搜索)算法。

思路

当我们搜索到了某一个点,有这样3种情况:

1.当前我们所在的格子就是终点。

2.如果不是终点,我们枚举向上、向下、向左、向右四个方向,依次去判断它旁边的四个点是否可以作为下一步合法的目标点,如果可以,那么我们就进行这一步,走到目标点,然后继续进行操作。

3.当然也有可能我们走到了“死胡同”里(上方、下方、左方、右方四个点都不是合法的目标点),那么我们就回退一步,然后从上一步所在的那个格子向其他未尝试的方向继续枚举。

怎样才能算“合法的目标点”?

1.必须在所给定的迷宫范围内

2.不能是迷宫边界或墙。

3.这个点在搜索过程中没有被走过(这样做是因为,如果一个点被允许多次访问,那么肯定会出现死循环的情况——在两个点之间来回走。)

实现代码

#include <iostream>
using namespace std;
int n, m;
string maze[105];
int sx, sy;
bool vis[105][105];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};//四个方向的方向数组
bool in(int x, int y) {
    return 0 <= x && x < n && 0 <= y && y < m;
}
bool dfs(int x, int y) {
    vis[x][y] = 1;//点已走过标记
    if (maze[x][y] == 'T') {//到达终点
        return 1;
    }
    for (int i = 0; i < 4; ++i) {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if (in(tx, ty) && !vis[tx][ty] && maze[tx][ty] != '*') {
            /*
            1.in(tx, ty) : 即将要访问的点在迷宫内
            2.!vis[tx][ty] : 点没有走过
            3.maze[tx][ty] != '*' : 不是墙
            */
            if (dfs(tx, ty)) {
                return 1;
            }
        }
    }
    return 0;
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> maze[i];
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if (maze[i][j] == 'S') {
                //记录起点的坐标
                sx = i;
                sy = j;
            }
        }
    }
    if (dfs(sx, sy)) {
        puts("Yes");
    } else {
        puts("No");
    }
    return 0;
}

深搜的剪枝优化

可行性剪枝

剪枝,顾名思义,就是通过一些判断,砍掉搜索树上不必要的子树。有时候,在搜索过程中我们会发现某个结点对应的子树的状态都不是我们要的结果,那么我们其实没必要对这个分支进行搜索,直接“砍掉”这棵子树(直接return退出),就是"剪枝"。

我们举一个例子:

给定n个整数,要求选出K个数,使得选出来的K个数的和为sum。

深度优先搜索算法-dfs讲解_子树

如上图,当k=2的时候,如果已经选了2个数,再往后选更多的数是没有意义的。所以我们可以直接减去这个搜索分支,对应上图中的剪刀减去的那棵子树。

又比如,如果所有的数都是正数,如果一旦发现当前和的值都已经大于sum了,那么之后不管怎么选,选择数的和都不可能是sum了,就可以直接终止这个分支的搜索。

例:从1,2,3,⋯,30这30个数中选8个数,使得和为200。

我们可以加如下剪枝

if (数字个数 > 8) return ;
if (总和 > 200) return ;

经过尝逝后发现:

没有剪枝

深度优先搜索算法-dfs讲解_最优性_02


加剪枝:

深度优先搜索算法-dfs讲解_搜索_03


最优性剪枝

我们再看一个问题:

有一个n×m大小的迷宫。其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地。你需要从S出发走到T,每次只能向上下左右相邻的位置移动,并且不能走出地图,也不能走进墙壁。保证迷宫至少存在一种可行的路径,输出S走到T的最少步数。

深度优先搜索算法-dfs讲解_C++_04

对于求最优解(从起点到终点的最小步数)这种问题,通常可以用最优性剪枝,比如在求解迷宫最短路的时候,如果发现当前的步数已经超过了当前最优解,那从当前状态开始的搜索都是多余的,因为这样搜索下去永远都搜不到更优的解。通过这样的剪枝,可以省去大量冗余的计算。

此外,在搜索是否有可行解的过程中,一旦找到了一组可行解,后面所有的搜索都不必再进行了,这算是最优性剪枝的一个特例。

现在我们考虑用dfs来解决这个问题,第一个搜到的答案res并不一定是正解,但是正解一定小于等于res。于是如果当前步数大于等于res就直接剪枝。

在dfs函数内加入如下代码

if (目前步数 >= res) return ;
if (目前所处的位置字符 == 'T') {
    答案 = 目前步数;//因为我们在刚才已经进行了一次剪枝,所以我们现在是可以保证目前答案大于之前答案的
    return ;
}

好啦,到这里就结束了捏~

求赞qwq

标签:剪枝,return,字符,int,迷宫,dfs,搜索,搜索算法,讲解
From: https://blog.51cto.com/u_16165905/6517545

相关文章

  • HDFS相关概念
    他的块比一般的大,为什么要这么设计缺点:(块不是越大越好)块设计的好处HDFS两大组件:元数据:......
  • 分布式文件系统HDFS简介
    HDFS实现目标:兼容廉价的硬件设备  支持大数据集  实现流数据读写  支持简单的文件模型  强大的跨平台兼容性自身的局限性:不适合低延迟的数据访问  无法高效储存大量小文件 不支持多用户写入及任意修改文件......
  • FastDFS单机版安装
    FastDFS6.9.5单机版安装一、下载需要的安装包cd/usr/local/src#下载fastdfs依赖库wgethttps://github.com/happyfish100/libfastcommon/archive/refs/tags/V1.0.67.tar.gzmvV1.0.67.tar.gzlibfastcommon-1.0.67.tar.gz#下载网络框架https://github.com/happyfish100......
  • hdfs的透明加密记录
    1、背景我们知道,在hdfs中,我们的数据是以block块存储在我们的磁盘上的,那么默认情况下,它是以密文存储的,还是以明文存储的呢?如果是明文存储的,那么是否就不安全呢?那么在hdfs中是如何做才能做到数据的透明加密呢?2、常见的加密层级应用层加密:这是最安全和最灵活的方法。加密内容最......
  • hdfs的透明加密记录
    1、背景我们知道,在hdfs中,我们的数据是以block块存储在我们的磁盘上的,那么默认情况下,它是以密文存储的,还是以明文存储的呢?如果是明文存储的,那么是否就不安全呢?那么在hdfs中是如何做才能做到数据的透明加密呢?2、常见的加密层级应用层加密:这是最安全和最灵活的方法。加密内容最......
  • 监听Activity生命周期方式及案例讲解
    本篇文章主要讲解如何快速实现Activity生命周期监听,以及其在官方lifecycle、第三方库Glide、PermissionX中的应用1.Activity生命周期监听Fragment实现Activity生命周期监听众所周知,Fragment中生命周期分发主要是依赖Activity,所以为了监听Activity的生命周期我们直接添加一个空的Fr......
  • 中视频不同类型的文案讲解
    文案的类型各不相同,因此写作要求也有所不同。中视频运营者想要通过不同类型的文案更好地传递信息给用户,就需要了解各种类型文案的写作技巧。下面我将详细介绍八种不同类型中视频文案的写作方法。(腾讯|课堂搜|索“如何运营视频才能获得百万粉丝”)一、主题文案:简明扼要、直截了当......
  • 2023跟我一起学设计模式:Golang 抽象工厂模式讲解和代码示例
    Golang抽象工厂模式讲解和代码示例抽象工厂是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。抽象工厂定义了用于创建不同产品的接口,但将实际的创建工作留给了具体工厂类。每个工厂类型都对应一个特定的产品变体。在创建产品时,客户端代码调用的是工厂对象的......
  • vite配置讲解
    Vite的配置文件通常被命名为vite.config.js,它是一个Node.js模块,导出一个对象,包含了各种配置选项。以下是一些常见的配置选项:mode:指定应用程序的模式,可以是开发模式('development')或生产模式('production')。在开发模式下,Vite会启用一些调试工具和优化,而在生产模式下,会进......
  • fastdfs配置文件说明
    参考网址一、tracker.conf#配置tracker.conf文件是否生效false生效true屏蔽disabled=false#程序的监听地址,如果不设定则监听所有地址(0.0.0.0)bind_addr=#tracker监听的端口port=22122#连接超时时间(秒)。#默认值为30。#注意:在内网(LAN)中,2秒就足够......