深度优先搜索(DFS),看这一篇就够了。

article/2025/9/13 18:34:46

一,定义:

深度优先搜索的思路和树的先序遍历很像,下面是百度百科上的定义:

深度优先遍历图的方法是,从图中某顶点v出发:

(1)访问顶点v;

(2)依次从v的未被访问的邻接点出发,对图进深度优先遍历;直至图中和v有路径相通的顶点都被访问;

(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。 当然,当人们刚刚掌握深度优先搜索的时候常常用它来走迷宫.事实上我们还有别的方法,那就是广度优先搜索(BFS).

 对于定义的理解,可以结合斐波那契数列(虽然用递归来写斐波那契是一种很糟糕的写法)来进行理解,如下图:

其中,右边这个树上的顺序是这样的:

        可以结合遍历的思想来理解DFS;

        DFS的题目大致可以分为两类:

1,对图的连通性进行检验:如迷宫问题,图的条件搜索。

2,DFS搜索顺序和规则问题,通过你穷举所有答案,找出符合条件的解。即爆搜问题。

         看到这里,你可能会有些疑惑具体是怎样的问题,本文就针对DFS的原理进行,常见的题型进行了总结,附上代码和解题思路。

二,原理与分析

1,DFS连通性分析:

在测试连通性是,DFS的思路是与人们的思想是一致的,在一条路上,我是否可以在这条路上一直走下去,如果走不通,那我就返回原来的节点,换个方向,再沿着一条路走下去,直到成功。

针对连通性问题,我们还可以再进行分类 :

1,无需回溯

        在这种问题,只需要在每一步中,将搜索到的节点抛弃掉,对于当前搜索到的节点进行计数,最终统计所有能到达的点。

        下面给出两个例题:
        例1,红与黑(简单)

/** @Author: your name* @Date: 2022-02-11 13:39:15* @LastEditTime: 2022-02-11 13:56:51* @LastEditors: Please set LastEditors* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: \All code\26.cpp*/
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100
char mp[MAXN][MAXN];
bool vis[MAXN][MAXN];
int W, H;
int ans;
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };
void init()
{for (int i = 0; i < MAXN; i++){for (int j = 0; j < MAXN; j++){vis[i][j] = false;}}ans = 1;//由于在初始点位置,所以一开始就是一块黑砖
}
void dfs(int x, int y)//常用DFS套路
{for (int i = 0; i < 4; i++){int newx = x + dx[i];int newy = y + dy[i];if (newx >= 1 && newx <= H && newy >= 1 && newy <= W && mp[newx][newy] == '.' && vis[newx][newy] == false){ans++;vis[newx][newy] = true;dfs(newx, newy);}}
}
int beginx, beginy;
int main()
{while (1){cin >> W >> H;if (W == 0 && H == 0){break;}init();for (int i = 1; i <= H; i++)for (int j = 1; j <= W; j++){cin >> mp[i][j];if (mp[i][j] == '@'){beginx = i;beginy = j;}}dfs(beginx, beginy);cout << ans << endl;}return 0;
}

例2,Lake Counting(染色法简单)

/** @Author: your name* @Date: 2022-01-22 21:06:31* @LastEditTime: 2022-01-22 21:32:59* @LastEditors: Please set LastEditors* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: \All code\9.cpp*/
#include <bits/stdc++.h>
using namespace std;
#define MAXN 105
bool vis[MAXN][MAXN];
char mp[MAXN][MAXN];
int row,cow;
int dx[8] = {0, 0, -1, 1, -1, -1, 1, 1};
int dy[8] = {-1, 1, 0, 0, -1, 1, -1, 1};
void dfs(int x,int y)
{vis[x][y] = 1;for(int i =0;i<8;i++){int newx = x+dx[i];int newy = y+dy[i];if(newx>=0&&newy>=0&&newx<row&&newy<cow&&mp[newx][newy] == 'W'&&vis[newx][newy] ==0){dfs(newx,newy);}}
}int main()
{int i, j;int count =0;cin >> row >> cow;for (i = 0; i < row; i++)for (j = 0; j < cow; j++){cin >> mp[i][j];}for (i = 0; i < row; i++)for (j = 0; j < cow; j++){if(mp[i][j] == 'W'&&vis[i][j] == 0)//碰到一个后,就最这个区域进行DFS,并进行标记{dfs(i,j);count++;}}cout<<count;return 0;
}

有了这两个粒例子,相信大家开始对DFS有了一个大概的认识。

二,需要回溯:迷宫类问题

        这一类问题就像我在刚刚导论里面提及的,如果这条路不同,则需要设置回溯,进行恢复现场。最经典的莫过于走迷宫问题(简单):

/** @Author: your name* @Date: 2022-01-22 21:06:31* @LastEditTime: 2022-01-22 22:12:20* @LastEditors: Please set LastEditors* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: \All code\9.cpp*/
#include <bits/stdc++.h>
using namespace std;
#define MAXN 505
bool vis[MAXN][MAXN];
char mp[MAXN][MAXN];
int row, cow;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
bool flag = false;
void dfs(int x, int y)
{vis[x][y] = 1;for (int i = 0; i < 4; i++){int newx = x + dx[i];int newy = y + dy[i];if (newx >= 0 && newy >= 0 && newx < row && newy < cow && vis[newx][newy] == 0){if (mp[newx][newy] == '.')dfs(newx, newy);if (mp[newx][newy] == 'E')flag = true;}}
}
void init()
{for (int i = 0; i < row; i++)for (int j = 0; j < cow; j++){vis[i][j] = 0;}
}
int main()
{int i, j;int count = 0;int row_s, cow_s;while (cin >> row >> cow){flag = false;for (i = 0; i < row; i++)for (j = 0; j < cow; j++){cin >> mp[i][j];if (mp[i][j] == 'S'){row_s = i;cow_s = j;}}init();dfs(row_s, cow_s);if (flag == true){cout << "Yes" << endl;}else{cout << "No" << endl;}}return 0;
}

除此之外,还有一个例题,用于计数的:马走日​​​​​​​​​​​​

       这个题只是将迷宫问题的路径选择和成功条件进行了一个改变,也是简单的搜索问题:

/** @Author: your name* @Date: 2022-02-11 14:44:55* @LastEditTime: 2022-02-11 15:30:56* @LastEditors: Please set LastEditors* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE* @FilePath: \All code\28.cpp*/
#include <iostream>
using namespace std;
#define MAXN 15
int mp[MAXN][MAXN];
bool vis[MAXN][MAXN];
int row, cow, newx, newy;
int ans;
int dx[8] = { -2, -2, 2, 2, -1, -1, 1, 1 };
int dy[8] = { 1, -1, 1, -1, -2, 2, -2, 2 };
void init()
{for (int i = 0; i < MAXN; i++){for (int j = 0; j < MAXN; j++){vis[i][j] = false;}}ans = 0;
}void dfs(int x, int y, int depth)
{for (int i = 0; i < 8; i++){int newx = x + dx[i];int newy = y + dy[i];if (newx >= 1 && newx <= row && newy >= 1 && newy <= cow && vis[newx][newy] == false){vis[newx][newy] = true;if (depth == row * cow - 1)//由于这里是newx,并不是已经递归到新一层中,所以这里需要减1{ans++;}dfs(newx, newy, depth + 1);vis[newx][newy] = false;}}
}int main()
{int T;cin >> T;while (T--){cin >> row >> cow >> newx >> newy;init();vis[newx + 1][newy + 1] = true;dfs(newx + 1, newy + 1, 1);//这里要注意,我在从第一步开始的时候,就已经算是处在第一层了,不是第0层。cout << ans << endl;}return 0;
}

         我在开始这一道题的时候,就犯下了这样一个错误:将截止条件的层数看错。大家在写代码的时候要注意这个问题。

总结一下DFS的模板

function dfs(当前状态){
    if(当前状态 == 目的状态){
        ···
    }
    for(···寻找新状态){
        if(状态合法){
            vis[访问该点];
            dfs(新状态);
            ?是否需要恢复现场->vis[恢复访问]
        } 
    }
    if(找不到新状态){
        ···
    }
}

 2,穷举解决问题

例:部分和问题:

 

 

#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[35];bool dfs(int id, int sum)
{if (sum == k)return 1;if (id > n)return 0;if (1 == dfs(id + 1, sum + a[id]))return 1;if (1 == dfs(id + 1, sum))return 1;return 0;
}int main()
{cin >> n >> k;for (int i = 1; i <= n; i++){cin >> a[i];}if (1 == dfs(1, 0)){puts("YES");}else{puts("NO");}return 0;
}

也很简单,只要一次在每一步中讨论当前这个数是否选取,即可。对于这个问题,如果是想要找出所有相加为k的序列中,个数最少的一个,也可以利用穷举的方法来写。同时也可以利用动态规划的知识来解题。读者可以下去自己思考。


http://chatgpt.dhexx.cn/article/S8KvxcdE.shtml

相关文章

Python实现深度优先遍历(DFS)和广度优先遍历(BFS)

一&#xff0c;简介 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法&#xff0c;生产上广泛用于拓扑排序&#xff0c;寻路(走迷宫)&#xff0c;搜索引擎&#xff0c;爬虫等&#xff0c;也频繁出现在 leetcode&am…

算法数据结构——图的遍历之深度优先搜索算法(Depth First Search)

1. 深度优先搜索简介 深度优先搜索算法&#xff08;Depth First Search&#xff09;&#xff1a;英文缩写为 DFS。是一种用于搜索树或图的算法。所谓深度优先&#xff0c;就是说每次都尝试向更深的节点走。 深度优先搜索采用了回溯思想&#xff0c;该算法沿着树的深度遍历树的节…

【新书速递】实用安全多方计算导论

安全多方计算&#xff08;MPC&#xff09;是解决数据安全与隐私保护问题的关键安全数据交换技术&#xff0c;近年来发展迅速&#xff0c;但由于MPC涉及复杂的密码学和工程实现技术&#xff0c;行业长期缺乏同时具备MPC研究、应用和实现能力的综合性人才&#xff0c;这阻碍了MPC…

百万富翁问题--安全多方计算

百万富翁问题—安全多方计算 是由图灵奖获得者姚期智提出的。 有A、B两个富翁&#xff0c;A资产i亿元&#xff0c;B资产j亿元&#xff0c;i、j均在0-10范围内&#xff0c;在互不让对方知道自己资产的情况下&#xff0c;比较A和B的资产谁多谁少。 那么如何去比较呢&#xff1f;…

隐私保护技术之安全多方计算

安全多方计算(Secure Multi-Party Computation&#xff0c;SMPC)用于解决一组互不信任的参与方各自持有秘密数据&#xff0c; 协同计算一个既定函数的问题。安全多方计算在保证参与方获得正确计算结果的同时&#xff0c;无法获得计算结果之外的任何信息。 在整个计算过程中&…

基于同态加密体制的安全多方计算

本文首发公众号VenusBlockChain&#xff0c;关注公众号后可免费阅读&#xff01;VenusBlockChain致力于区块链技术研究&#xff0c;传播区块链技术和解决方案、区块链应用落地、区块链行业动态等。有兴趣的小伙伴们&#xff0c;欢迎关注。 安全多方计算&#xff08;Secure Mu…

多方安全计算

说明&#xff0c;本文是转载的&#xff0c;个人觉得作者讲解清晰明了&#xff0c;收录用于学习&#xff0c;原文链接&#xff1a;https://blog.csdn.net/yuxinqingge/article/details/104588197。 如今&#xff0c;互联网已经完成了从IT时代向DT时代转变&#xff0c;数据已经成…

多方安全计算MPC

1.多方安全计算的价值 MPC是密码学的一个重要分支&#xff0c;旨在解决一组互不信任的参与方之间保护隐私的协同计算问题&#xff0c;为数据需求方提供不泄露原始数据前提下的多方协同计算能力。 在目前个人数据毫无隐私的环境下&#xff0c;对数据进行确权并实现数据价值显得…

安全多方计算(MPC)

MPC既适用于特定的算法&#xff0c;如加法、乘法、AES&#xff0c;集合交集等&#xff1b;也适用于所有可表示成计算过程的通用算法。 根据计算参与方个数不同&#xff0c;可分为只有两个参与方的2PC和多个参与方&#xff08;≥3&#xff09;的通用MPC 1&#xff09;安全两方&a…

安全多方计算之二:一文搞懂百万富翁问题

百万富翁问题 1. 解决方案2. 协议描述3. 协议说明4. 协议举例5. 协议扩展 两个百万富翁Alice和Bob想知道他们两个谁更富有&#xff0c;但他们都不想让对方及其他第三方知道自己财富的任何信息&#xff0c;这是由中国计算机科学家、2000年图灵奖获得者姚启智教授于1982年在论文《…

安全多方计算-入门学习笔记(三)

四、基于非噪音的安全多方计算介绍 1概念 非噪音方法一般是通过密码学方法将数据编码或加密&#xff0c;得到一些奇怪的数字&#xff0c;而且这些奇怪的数字有一些神奇的性质&#xff0c;比如看上去很随机但其实保留了原始数据的线性关系&#xff0c;或者顺序明明被打乱但人们…

基于隐私保护的安全多方计算区块链融合技术的智能合约

安全多方计算与区块链的融合技术 SMPC-安全多方计算综述安全的定义安全模型SMPC效率问题 区块链应用&#xff1a;智能合约智能合约的问题SMPC需求引入 基于SMPC的智能合约辅助&#xff1a;MPI协议-效率提升 SMPC-安全多方计算综述 随着物联网、移动计算、大数据、云计算的快速…

安全多方计算之GMW协议

安全多方计算之GMW协议 一、背景 论文原文《How to play any mental game》。作者&#xff1a;Micali S, Goldreich O, Wigderson A. 发表在&#xff1a;Proceedings of the Nineteenth ACM Symp on Theory of Computing, STOC. GMW协议可以同时适用在布尔电路和算术电路&am…

多方安全计算概述

多方安全计算&#xff08;Secure Multi-Party Computation&#xff0c; MPC&#xff09;是密码学的一个分支&#xff0c;在无可信第三方的情况下&#xff0c;仍可安全地按照公开的计算逻辑&#xff0c;进行数据协同计算&#xff0c;并输出结果。 即使参与各方输入的数据只有自…

安全多方计算之一:什么是安全多方计算

安全多方计算 1. 安全多方计算简介2. 基本概念2.1 参与者2.2 攻击者 3. 安全多方计算的模型4. 安全多方计算的密码学工具5. 学习参考 1. 安全多方计算简介 安全多方计算问题SMC&#xff0c;Secure Multi-party Computation&#xff09;由由中国计算机科学家、2000年图灵奖获得…

安全多方计算简介

文章目录 一、安全多方计算定义二、安全多方计算安全模型1.行为模型2.安全门限 三、安全多方计算关键技术1.秘密共享(Secret Sharing, SS)2.不经意传输(Oblivious Transfer, OT)3.混淆电路(Garbled Circuit, GC) 参考资料 一、安全多方计算定义 安全多方计算(Secure Multi-Par…

安全多方计算 # 个人笔记

一个优美令人如痴如醉的领域。 Data is the oil of the 21st century 欢迎读者拍砖和提供本文修改建议。本文长期维护。 第二次编辑于2021/10/20&#xff0c;新增了部分阅读材料&#xff0c;调整了文章内容的组织。 0 研究背景 【最后更新于2021/10/20】 时代背景&#xff1…

Verilog操作符

操作符优先级表 Verilog中的大小(size)与符号 Verilog根据表达式中变量的长度对表达式的值自动地进行调整; Verilog自动截断或扩展赋值语句中右边的值以适应左边变量的长度; 当一个负数赋值给无符号变量如reg时,Verilog自动完成二进制补码计算; 算术运算符 加(+)、减(-…

C语言——操作符详解

目录 一、算术操作符 二、移位操作符 三、位操作符 四、赋值操作符 五、单目操作符 六、关系操作符 七、逻辑操作符 八、条件操作符 九、逗号表达式 十、下标引用、函数调用和结构成员 以上就是C语言中涉及到得操作符&#xff0c;我将用代码实例配合说明&#xff0c…

教妹学Java(十一):操作符简介

大家好&#xff0c;我是沉默王二&#xff0c;一个和黄家驹一样身高&#xff0c;和刘德华一样颜值的程序员。本篇文章通过我和三妹对话的形式来谈一谈“Java 中的操作符”。 教妹学 Java&#xff0c;没见过这么有趣的标题吧&#xff1f;“语不惊人死不休”&#xff0c;没错&…