【算法学习笔记】17:DFS与BFS

2023-11-10

1 DFS

深度优先搜索常用于解决需要给出所有方案的问题,因为它的搜索顺序就是能够得到一个完整的搜索路径(方案)后回退再去搜索其它的方案。

1.1 例题:排列数字

由于要求所有排列的方案,可以每次从 1.. n 1..n 1..n里拿一个数字,然后记录一下这个数拿过了,再递归地搜索下一个数字,当所有数字都取完之后,就得到了一种方案,将这种方案输出,回退之后去搜下一个方案。

“回退之后去搜下一个方案”,其实就是在每层DFS的时候遍历一下所有的允许使用的数字,作为下一个位置的数字。需要注意的是在进入下一层DFS之前要把这个数放进 p a t h path path里去,并记录一下这个数用过了,然后在回退之后还要把 p a t h path path末尾加入的这个数删掉,并记录一下这个数现在是没使用的状态。

基于这个思想,实现的代码如下:

#include <iostream>
#include <vector>

using namespace std;

const int N = 10;

int n;
vector<int> path;
bool st[N];

void dfs() {
    if (path.size() == n) {
        for (int i = 0; i < n; i ++ ) cout << path[i] << ' ';
        cout << endl;
        return ;
    }
    for (int i = 1; i <= n; i ++ ) {
        if (!st[i]) {
            path.push_back(i);
            st[i] = true;
            dfs();
            path.pop_back();
            st[i] = false;
        }
    }
}

int main() {
    cin >> n;
    dfs();
    return 0;
}

这里是用一个vector存了 p a t h path path,然后在装满(装够 n n n个数)的时候把整个方案输出出来。如果用数组的话,可以将DFS进行到了哪一层(数组里放了多少数字)维护一下,可以放在参数里,也可以直接在全局维护。另外,这里是用一个bool类型的st数组维护了每个数字有没有被使用过,由于数的范围很小,还不到32位int,所以可以直接用一个int值来代替st,也就进行了二进制优化。

另外,将这个int值作为参数在DFS中进行值传递,就不会在下一层里对它有修改了,这样写起来也方便,这里的语义是表示一种“状态”。

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int path[N];
int n;

// 当前放置u位置的数字,目前所有数字使用状态是state
void dfs(int u, int state) {
    if (u == n) {
        for (int i = 0; i < n; i ++ ) cout << path[i] << ' ';
        cout << endl;
        return ;
    }
    for (int i = 1; i <= n; i ++ ) {
        if (state & (1 << i)) continue;
        path[u] = i;
        dfs(u + 1, state + (1 << i));
    }
}

int main() {
    cin >> n;
    // 从第0位置的数开始,状态一开始是全0,所有数字没使用
    dfs(0, 0);
    return 0;
}

1.2 例题:n-皇后问题

这个问题里,由于限制了每行、每列、每个对角线上都只能有一个'Q',所以可以把一些条件融入到搜索顺序中去。比如看到第一个条件“每行只能有一个'Q'”,所以可以DFS的每层搜索这一行的'Q'应该放在哪一列,然后再保证这一列没有放置过(用col数组检查),保证这个位置所在的主正反对角线(用dgudg数组检查)没有放置过。

如何知道一个位置是在哪个正反对角线上的?可以发现正反对角线上的元素 ( x , y ) (x, y) (x,y)的特征,分别是 x + y x + y x+y等于一个固定值,以及 x − y x - y xy等于一个固定值,所以可以开 2 ∗ n 2 * n 2n的一维数组dgudg记录一下。

#include <iostream>

using namespace std;

const int N = 12;

char g[N][N];
bool col[N], dg[2 * N], udg[2 * N];
int n;

// 当前搜索u号行
void dfs(int u) {
    if (u == n) {
        for (int i = 0; i < n; i ++ ) {
            for (int j = 0; j < n; j ++ )
                cout << g[i][j];
            cout << endl;
        }
        cout << endl;
        return ;
    }
    for (int j = 0; j < n; j ++ ) {
        if (col[j] || dg[u + j] || udg[u - j + n]) continue;
        col[j] = dg[u + j] = udg[u - j + n] = true;
        g[u][j] = 'Q';
        dfs(u + 1);
        col[j] = dg[u + j] = udg[u - j + n] = false;
        g[u][j] = '.';
    }
}


int main() {
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            g[i][j] = '.';
    dfs(0);
    return 0;
}

2 BFS

广度优先搜索每次扩展当前结点的所有结点,所以需要一个栈来维护要扩展的结点。由于广度优先搜索每次都是把所有能到的下一步搜完,所以能够得到最短 p a t h path path的解,所以一些不带权求最短路径的问题也可以直接用BFS解决。

注意,在扩展结点的某个下一结点(也就是把这个下一结点加入到队列)的时候,要保证这个结点是没被扩展过的,否则队列里就有重复结点了,就会出现重复访问了。

2.1 例题:走迷宫

这个就是一个不带边权的最短路问题,直接BFS搜一下就行了,遇到墙或者已经扩展过的点就不用扩展了,核心在与到扩展的点的最短距离,等于到当前点的最短距离+1,也就是dist[a][b] = dist[x][y] + 1

注意这里用dist中某个点值为-1表示这个点没有访问过,注意边界:到 ( 0 , 0 ) (0, 0) (0,0)位置自己的距离是0。

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

typedef pair<int, int> PII;

const int N = 110;

// 存图
int g[N][N];
// 四个方向
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
// (0, 0)到每个点的距离
int dist[N][N];

int main() {
    memset(dist, -1, sizeof dist);
    dist[0][0] = 0;
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
            cin >> g[i][j];
    // BFS
    queue<PII> q;
    q.push({0, 0});
    while (q.size()) {
        auto& t = q.front();
        q.pop();
        int x = t.first, y = t.second;
        for (int i = 0; i < 4; i ++ ) {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m || g[a][b] == 1 || dist[a][b] != -1)
                continue;
            dist[a][b] = dist[x][y] + 1;
            q.push({a, b});
        }
    }
    cout << dist[n - 1][m - 1] << endl;
    return 0;
}

2.2 例题:八数码

这个问题就是把整个九宫格的局面当成一个节点,给定一个局面,就能够知道如何转移到其它局面,以及给出了目标局面,要求最短转移是多少步:
在这里插入图片描述

所以要想个方法来记录局面,可以从上到下从左到右记录到一个字符串里(就相当于二维字符数组展品成了一维),由于知道行数为 3 3 3列数为 3 3 3,可以很方便的对两者的下标进行转换。

另外要记录到每个结点的距离,由于这里的结点是局面(字符串),不能像上一个题一样开个数组记录了,所以可以用哈希表来记录。

其它的和上一题没有本质区别,都是求无权最短路的问题,直接BFS。

#include <iostream>
#include <queue>
#include <unordered_map>

using namespace std;

int bfs(string state) {
    queue<string> q;
    // 记录路径长度
    unordered_map<string, int> d;
    // 起始状态放进去
    q.push(state);
    d[state] = 0;
    // 四个方向
    int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
    // 终止状态,搜索的目标状态
    string end = "12345678x";
    // BFS搜索过程
    while (q.size()) {
        string t = q.front();
        q.pop();
        // 如果能到目标状态就返回距离
        if (t == end)
            return d[t];
        // 因为一会要直接在t上交换,所以这里记录一下到t的距离
        // 也可以直接把t这个当前状态复制一份
        int dist = d[t];
        // 找到x的下标,可以变换成(x, y)的二维形式
        int k = t.find('x');
        int x = k / 3, y = k % 3;
        // 四个方向搜
        for (int i = 0; i < 4; i ++) {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= 3 || b < 0 || b >= 3)
                continue;
            // 做交换
            swap(t[a * 3 + b], t[k]);
            // 如果交换后的状态没扩展过
            if (!d.count(t)) {
                // 就扩展一下,还要记录它扩展过了(写入距离)
                d[t] = dist + 1;
                q.push(t);
            }
            // 交换回来,以恢复t
            swap(t[a * 3 + b], t[k]);
        }
    }
    // 到这说明无法搜到end状态
    return -1;
}

int main() {
    char s;
    string state;
    // 读入开始局面,以字符串形式存储
    for (int i = 0; i < 9; i ++) {
        cin >> s;
        state += s;
    }
    // 这里把BFS封装起来了,不过没有递归不写到函数里也行
    cout << bfs(state) << endl;
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【算法学习笔记】17:DFS与BFS 的相关文章

  • 境界的彼方_lduoj_bfs宽搜

    Description wyy是一个著名动画 境界的彼方 的男主 此时他非常的慌张 因为女主栗山未来进入了境界的彼方内部 并且花费了大量的血量去拯救wyy wyy此时也进入了境界的彼方 他妈给了他一张地图去寻找境界的彼方的核心去拯救女主 现
  • 学渣带你刷Leetcode0017. 电话号码的字母组合

    题目描述 给定一个仅包含数字 2 9 的字符串 返回所有它能表示的字母组合 给出数字到字母的映射如下 与电话按键相同 注意 1 不对应任何字母 示例 输入 23 输出 ad ae af bd be bf cd ce cf 说明 尽管上面的答
  • 基础算法题——德邦国王(dfs、剪枝)

    德邦国王 题目还算中规中矩 就是剪枝比较麻烦 解题思路 dfs 剪枝 移动次数不超过设定值 M 若有解 则后面的步骤不可大于该解的值 不断查询完美矩阵与当前矩阵不同的个数 t t 1 为最快可将当前矩阵移动成完美矩阵的步数 若 t 1 已经
  • UVA12166 Equilibrium Mobile

    VJ传送门 一道思维题 刚开始看的时候没什么思路 在博客园上参考了大佬的解析 在这里总结一下 一 分析 这道题要求让天平平衡所需要的最小改动次数 至少有一个不变 我们可以先选定一个不变的基准 然后改变其他的秤砣 得到以此为基准的天平的总重量
  • 关于multipartFile.transferTo方法报错java.nio.file.FileAlreadyExistsException

    之前老项目用的spring4版本 现在升级成spring5版本 重新把文件中心搬过来 发现原先有一段 MultipartFile multiFile XXX File file File createTempFile System curr
  • 深度优先搜索算法(DFS)原理及示例详解

    目录 1 算法原理 2 基本思路 980 不同路径 题目描述 输入输出示例 直观思路 代码实现 1 算法原理 事实上 深度优先搜索属于图算法的一种 英文缩写为DFS即Depth First Search 其过程简要来说是对每一个可能的分支路
  • PowerOJ2512: 小红灌溉【染色】

    题目链接 划重点 每个有菜的点只能浇一次且恰好一次 所以意思就是 譬如某个菜的位置是 x y 那么 行x 列y的浇水方案只能使用其中的一个 以此类推 我们给每个有蔬菜的位置的 x y 的x点与y点链接一条无向边 代表x和y只能选择其中的一个
  • LeetCode0752-打开转盘锁

    LeetCode0752 打开转盘锁 题目 你有一个带有四个圆形拨轮的转盘锁 每个拨轮都有10个数字 0 1 2 3 4 5 6 7 8 9 每个拨轮可以自由旋转 例如把 9 变为 0 0 变为 9 每次旋转都只能旋转一个拨轮的一位数字 锁
  • 第十届蓝桥杯省赛C++B组 迷宫

    试题 E 迷宫 本题总分 15 分 问题描述 下图给出了一个迷宫的平面图 其中标记为 1 的为障碍 标记为 0 的为可 以通行的地方 010000 000100 001001 110000 迷宫的入口为左上角 出口为右下角 在迷宫中 只能从
  • Codeforces-1454E Number of Simple Paths(基环树-思维)

    题目大意 给你n个点 n条边 求图中简单路径的个数 题目思路 n个点n条边 那么图中一定有一个环 拿这个图来讲 我们将两点间的关系分为4种 1 两点都在环上 简单路径的个数为2 例如2与5 2 一个点在环上一个点不在环上 简单路径个数为2
  • Knight Moves_dfs_2018_3_10

    A friend of you is doing research on the Traveling Knight Problem TKP where you are to find the shortest closed tour of
  • 题目 1548: [蓝桥杯][算法提高VIP]盾神与砝码称重

    时间限制 1Sec 内存限制 128MB 提交 782 解决 331 题目描述 有一天 他在宿舍里无意中发现了一个天平 这 个天平很奇怪 有n个完好的砝码 但是没有游码 盾神为他的发现兴奋不已 于是他准备去称一称自己的东西 他准备好了m种物
  • Leetcode【DFS BFS】

    Leetcode 200 岛屿数量 题目 解题 思路 DFS解法 BFS解法 题目 给你一个由 1 陆地 和 0 水 组成的的二维网格 请你计算网格中岛屿的数量 岛屿总是被水包围 并且每座岛屿只能由水平方向和 或竖直方向上相邻的陆地连接形成
  • 链式前向星存树图和遍历它的两种方法【dfs、bfs】

    目录 一 链式前向星存图 二 两种遍历方法 一 链式前向星存图 n个点 n 1条边 链式前向星把上面的树图存下来 输入 9 代表要存进去n个点 1 2 下面是n 1条边 每条边连接两个点 1 3 1 7 2 4 4 5 4 6 3 8 3
  • BFS(广度优先算法)——判断无向简单图中任意两点是否连通

    include
  • 数据结构——图的DFS(深度优先遍历)- C语言代码实现

    图的深度优先遍历的基本思想 从图中某顶点v出发 1 访问顶点v 2 依次从v的未被访问的邻接点出发 对图进行深度优先遍历 直至图中和v有路径相通的顶点都被访问 3 若此时图中尚有顶点未被访问 则从一个未被访问的顶点出发 重新进行深度优先遍历
  • 无向图染色问题-dfs剪枝

    无向图染色问题 问题描述 给定一个无向图 要求用最少的颜色将节点染色 限制是不能让相邻节点染上相同的颜色 算法 使用dfs 为节点分配不同的颜色进行尝试 计算每种分配所需的颜色数 最终进行回溯 取得最小的颜色数 代码 C include
  • 迷宫问题java老鼠走迷宫(回溯法,递归,二维数组)(超级容易理解)

    回溯法迷宫问题 思路 利用回溯法和递归思想解决 findWay 方法就是专门来找出迷宫的路径 如果找到 就返回 true 否则返回 false map 就是二维数组 即表示迷宫 i j 就是老鼠的位置 初始化的位置为 1 1 因为我们是递归
  • 数据结构--树存储结构 & 深度优先遍历 & 广度优先遍历 通俗易懂

    树的概念 首先 树是一种常用的非线性数据结构 是以边 Edge 相连的节点 Node 的集合 每个节点存储对应的值 当存在子节点时与之相连 根节点 是树的首个节点 边 所有节点都由边相连 用于标识节点间的关系 叶子结点 树的末端节点 它们没
  • 设计与算法:迷宫问题

    描述 定义一个二维数组 int maze 5 5 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 它表示一个迷宫 其中的1表示墙壁 0表示可以走的路 只能横着走或竖着走 不能斜着走 要求编

随机推荐

  • Unity的C#编程教程_56_Namespace 详解

    文章目录 Namespaces Tour of Namespaces Namespaces 命名空间使得我们可以组织和管理我们的代码库 假设我们设置一个脚本名叫 Weapon using System Collections using S
  • python整段代码注释-Python中注释(多行注释和单行注释)的用法实例

    Python中注释 多行注释和单行注释 的用法实例 发布时间 2020 09 30 23 18 32 来源 脚本之家 阅读 97 前言 学会向程序中添加必要的注释 也是很重要的 注释不仅可以用来解释程序某些部分的作用和功能 用自然语言描述代
  • main.c:9:21: fatal error: sqlite3.h: 没有那个文件或目录

    今天在 Ubuntu 里看别人代码时 头文件里面有个
  • 2023普华永道中国首席数据官调研

    导读 在中国2 500家最大的上市企业中 首席数据官或类似管理岗的渗透率仅为1 3 远低于全球27 的水平 首席数据官的推广任重道远 其中 金融行业和通讯 媒体与科技行业的首席数据官或类似管理岗的数量位居前两位 也与这几个行业的数字化转型发
  • 【100天精通Python】Day48:Python Web开发_WSGI网络服务器网关接口与使用

    目录 1 WSGI接口 1 1 CGI 简介 1 2 WSGI 简介 1 3 定义 WSGI 接口 1 3 1 应用程序 Application 1 3 2 服务器 Server 1 4 WSGI 接口的使用示例 1 5 WSGI接口的优势
  • bp网络拟合函数 matlab_基于RBF神经网络的曲线拟合

    目前 在人工神经网络的实际应用中 绝大部分的神经网络模型是采用误差逆传播 error BackPropagation BP 网络和它的变化形式径向基函数 Radial Basis Function RBF 神经网络 RBF网络是一种高效的前
  • 微信小程序使用image组件显示图片的方法

    本文实例讲述了微信小程序使用image组件显示图片的方法 分享给大家供大家参考 具体如下 1 效果展示 2 关键代码 index wxml 代码如下
  • Lightgbm 直方图优化算法深入理解

    一 概述 在之前的介绍Xgboost的众多博文中 已经介绍过 在树分裂计算分裂特征的增益时 xgboost 采用了预排序的方法来处理节点分裂 这样计算的分裂点比较精确 但是 也造成了很大的时间开销 为了解决这个问题 Lightgbm 选择了
  • ubuntu16.04 使用astra s摄像头

    Astra相机使用方法 官网链接 https orbbec3d com develop Astra相机 GitHub orbbec ros astra camera ROS wrapper for Astra camera 普通相机 Git
  • mac安装lrzsz后运行卡死解决办法

    lrzsz的安装配置具体参见 https segmentfault com a 1190000012166969 上述完成后 若可以正常使用 万事大吉 如出现卡死的情况 可以查看配置文件 usr local bin iterm2 recv
  • openwrt 之通过uci 设置参数

    在openwrt中 默认一种配置文件 默认的路径 etc config 在这里面的所有配置文件如需要修改只需使用uci 这个指令来修改 以下uci 指令参数 root xxxx uci Usage uci
  • ubuntu自带vim配色方案

    系统版本 ubuntu 16 04 LTS 刚开始用vim的时候 大家可能会觉得默认的语法高亮的颜色不合心意 不过对于vim来说 这并不是一个问题 其实vim的配色方案是可以更改的 既可以选择系统自带的配色方案 也可以从网上下载其它配色方案
  • 简单理解Hadoop(Hadoop是什么、如何工作)

    一 Hadoop主要的任务部署分为3个部分 分别是 Client机器 主节点和从节点 主节点主要负责Hadoop两个关键功能模块HDFS Map Reduce的监督 当Job Tracker使用Map Reduce进行监控和调度数据的并行处
  • linux下部署thinkphp5项目

    准备工作 购买一个linux服务器地址 安装好linux常用的ssh工具 我这边喜欢用xshell敲命令 用filezilla传输文件 这些工具只要到官网下载就好 速度很快的 1 安装phpstudy for linux 安装下载phpst
  • java:JSONArray转byte[]字节数组

    package com xxx huali hualitest json import com alibaba fastjson JSONArray import com alibaba fastjson util Base64 publi
  • C语言运行流程

    在上一篇文章visual studio如何运行并调试C语言代码中写了如何运行并调试代码 我们就明确一个事实 即不论是嵌入式系统 亦或是普通PC电脑 对于程序的运行硬件处理器只能识别0 1的二进制码 从类人语言的C代码 需要经过一系列的转换过
  • 各种算法使用场景

    深度优先搜索BFS VS 广度优先搜索 DFS 算法就是回溯算法 BFS 相对 DFS 的最主要的区别是 BFS 找到的路径一定是最短的 但代价就是空间复杂度可能比 DFS 大很多 递归灵魂三问 labuladong 告诉你 遇到任何递归型
  • SQL Server基础Sql语句复习

    基础至极 1 创建表 create table Course Cno char 4 primary key not null 创建主键 非空 Cname char 40 not null Cpno char 4 Ccredit smalli
  • 软件测试报告bug统计,软件测试中如何有效地写Bug报告

    引言 为公众写过软件的人 大概都收到过很拙劣的bug 计算机程序代码中的错误或程序运行时的瑕疵 译者注 报告 例如 在报告中说 不好用 所报告内容毫无意义 在报告中用户没有提供足够的信息 在报告中提供了错误信息 所报告的问题是由于用户的过失
  • 【算法学习笔记】17:DFS与BFS

    1 DFS 深度优先搜索常用于解决需要给出所有方案的问题 因为它的搜索顺序就是能够得到一个完整的搜索路径 方案 后回退再去搜索其它的方案 1 1 例题 排列数字 由于要求所有排列的方案 可以每次从 1 n 1 n 1 n里拿一个数字 然后记