【DFS(深度优先搜索)详解】看这一篇就够啦

【DFS详解】看这一篇就够啦

  • 🍃1. 算法思想
  • 🍃2. 三种枚举方式
    • 🍃2.1 指数型枚举
    • 🍃2.2 排列型枚举
    • 🍃2.3 组合型枚举
  • 🍃3. 剪枝优化
  • 🍃4. 图的搜索
  • 🍃5. 来几道题试试手
    • 🍃5.1 选数
    • 🍃5.2 火柴棒等式

在这里插入图片描述

🚀欢迎互三👉: 2的n次方_💎💎
🚀所属专栏:数据结构与算法学习⭐⭐
在这里插入图片描述

🍃1. 算法思想

DFS算法的基本思想是从图中的某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,整个进程反复进行直到所有顶点都被访问为止。

在这里插入图片描述
从上图中可以直观的感受到这种思想
就是一条路走到黑的思想,走到无路可走再回退到上一层,再选择另一条路继续一直走,再回退,直到整个遍历完成,深度优先搜索一般是通过递归来实现的,“递”的过程就是往下搜的过程对应着深度,“归”的过程就是回溯,回退上一级

🍃2. 三种枚举方式

🍃2.1 指数型枚举

在这里插入图片描述
指数型枚举是指一共有n个数,每一个数都有两种状态,也就是选或不选,时间复杂度也就是2^n,指数级的
例如3个数的枚举时,每一个数都有选和不选两种状态,可以根据这个画出递归搜索树
在这里插入图片描述

接着用代码实现一下

#include <iostream>
using namespace std;
int n;
int vis[20];
void dfs(int x){//表示已经n个数都被判断过了,一种方案已经搜索完成if(x > n){for(int i = 1;i <= n;i++){if(vis[i] == 1)cout<<i<<" ";}cout<<'\n';return;}vis[x] = 2;//表示不选dfs(x+1);//继续搜下一个vis[x] = 0;//回溯vis[x] = 1;//表示选dfs(x+1);//继续搜下一个vis[x] = 0;//回溯}
int main(){cin>>n;dfs(1);return 0;
}

🍃2.2 排列型枚举

排列型枚举是一种生成给定集合所有可能排列的方法,其实在中学阶段我们就学过排列组合的问题,排列是区分顺序的,例如,同样是1 2 3三个数字,1,2, 3和 1,3,2是两种方案。
来看下面的一个例题:
在这里插入图片描述
很简单,就是生成n个数字的全排列方案,在用代码实现的过程中,需要另外再开一个vis数组,表示状态,以此来区分是否被选过

#include <bits/stdc++.h>
using namespace std;
int vis[15];
int a[15];
int n;
void dfs(int x){//表示n个数字都已经选过了if(x > n){for(int i = 1;i <= n;i++){cout<<setw(5)<<a[i];//题目中要求5个场宽}cout<<'\n';return;}for(int i = 1;i <= n;i++){if(!vis[i]){vis[i] = 1;//选过标记为1a[x] = i;//表示该数字被选上了dfs(x+1);//继续选下一个数字vis[i] = 0;//回溯重置该数字的状态a[x] = 0;//,也可以不写,因为数据可以直接覆盖}}
}int main(){cin>>n;dfs(1);return 0;
}

也就是依次枚举n个数,当这n个数选出一种方案之后,就回溯,再判断其它分支

🍃2.3 组合型枚举

组合是从n个不同元素中取出m(m≤n)个元素的所有取法,组合不考虑元素的顺序。也就是 1 2 3 和 1 3 2是同一种方案
在这里插入图片描述
这次的dfs中采用了两个参数,一个表示枚举了几个数,一个表示从哪个数开始往后选,因为这次是组合型枚举,例如,在选了1 3 2之前,1 2 3肯定也已经选过了,所以不会有1 3 2这种情况出现,从哪个数开始往后选,都是选的比这个数字典序大的数,不存在字典数大的数排在字典数小的之前的情况,所以要记录从哪个数开始往后选

#include <bits/stdc++.h>
using namespace std;
int a[25];
int n,r;
void dfs(int x,int start){//已经选够的情况if(x > r){for(int i = 1;i<=r;i++){cout<<setw(3)<<a[i];}cout<<'\n';return;}for(int i = start;i<=n;i++){a[x] = i;dfs(x+1,i+1);//选下一个数字,并且下一个数字的字典序要比本次大,也就是从i+1开始往后选a[x] = 0;}
}
int main(){cin>>n>>r;dfs(1,1);return 0;
}

🍃3. 剪枝优化

在深度优先搜索(DFS)中,剪枝是一种常用的优化技术,用于减少不必要的搜索空间,从而提高搜索效率。剪枝的核心思想是在搜索过程中,尽早地识别和排除那些不可能产生解的路径或状态,从而避免在这些无效路径上浪费时间和资源。
dfs(深度优先搜索)其实是一种特别暴力的算法,也就是我们常说的暴力搜索,时间复杂度一般都是指数级或阶乘级的这样,这时,剪枝就显得尤为重要,不然特别容易超时
在这里插入图片描述

来看一道洛谷的典型题:P1088:火星人
在这里插入图片描述

这题是不是就是我们之前讲到的全排列类型的题,意思就是给出一个排列方式,按照字典序求这种方式以后的第几种排列

这次用Java实现一下:

public class Main {static int n = 0, r = 0;//n个数字,求第r中排列方式static int cnt = 0;//记录次数static int[] arr = new int[10010];static int[] mars = new int[10010];//火星人的排列static boolean[] vis = new boolean[10010];//记录状态static boolean falg = false;//记录状态,后面用于剪枝public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();r = sc.nextInt();for (int i = 1; i <= n; i++) {mars[i] = sc.nextInt();}dfs(1);}public static void dfs(int x) {//剪枝,后面的不用再去排列了if (falg) {return;}if (x > n) {cnt++;if (cnt == r + 1) {//表示已经找到了答案falg = true;for (int i = 1; i <= n; i++) {System.out.print(arr[i] + " ");}System.out.println();}return;}//和之前写的排列模板一样for (int i = 1; i <= n; i++) {//表示从火星人给出的排列方案开始往后搜索if (cnt == 0) {i = mars[x];}if (!vis[i]) {arr[x] = i;vis[i] = true;dfs(x + 1);vis[i] = false;}}}
}

这道题我们就很好的利用了剪枝进行优化,不然按照原来算法的时间复杂度肯定是会超时的,当我们找到目标方案之后,后面的方案就没必要进行搜索了,此时直接退出函数,也就是剪枝
每一题的剪枝方案需要具体题目具体分析。

🍃4. 图的搜索

步骤:
1.选择起始点:从图的某个顶点v开始。
2.标记当前顶点:将当前顶点v标记为已访问,以避免重复访问。
3.遍历邻接点:对于v的每个未访问的邻接点w,递归地执行DFS,从w开始。
4.回溯:当没有更多的邻接点可以遍历时,返回到上一步的顶点。

下面看一道例题:

洛谷1683:入门

图的存储:通过二维数组进行存储
怎么往四个方向进行搜索:定义两个方向数组

在这里插入图片描述

#include <iostream>
using namespace std;
const int N = 25;
char arr[N][N];
bool vis[N][N];
int res;
int x, y;
//方向数组,四个方向进行搜索
int dx[4] = { -1,0,1,0 };
int dy[4] = { 0,1,0,-1 };
void dfs(int m,int n){for(int i = 0;i < 4;i++){int a = m + dx[i];int b = n + dy[i];if(vis[a][b]) continue;if(arr[a][b] != '.')continue;//只能走"."if(a < 0 || a >= x) continue;//不能越界if(b < 0 || b >= y) continue;vis[a][b] = true;res++;dfs(a,b);//本题不需要回溯,直接往下搜}
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> y >> x;for (int i = 0; i < x; i++) {for (int j = 0; j < y; j++) {cin >> arr[i][j];}}for (int i = 0; i < x; i++) {for (int j = 0; j < y; j++) {//从起点开始搜索if (arr[i][j] == '@') {vis[i][j] = true;dfs(i, j);}}}cout << res + 1;//加上起点return 0;
}

🍃5. 来几道题试试手

🍃5.1 选数

做题点这里👉 : 洛谷P1036

在这里插入图片描述

这道题其实还是之前讲过的组合型枚举,例如样例中是4个数里边选3个进行组合,只不过最后多了一个求和和素数判断

还用Java来实现一下:

public class Main {static int n = 0,k = 0;static int cnt = 0;static int[] arr = new int[50];static int[] res = new int[20];public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();k = sc.nextInt();for(int i = 1;i <= n;i++){arr[i] = sc.nextInt();}dfs(1,1);System.out.println(cnt);}public static boolean isPrime(int num){for(int i=2;i*i<=num;i++){if(num%i==0)return false;}return true;}public static void dfs(int x, int start){if(x == k + 1){int sum = 0;//求和for(int i =1;i <= k;i++){sum += res[i];}//判断素数,方案数+1if(isPrime(sum)){cnt++;}return;}for(int i = start;i <= n;i ++){res[x] = arr[i];dfs(x + 1,i + 1);res[x] = 0;}}
}

🍃5.2 火柴棒等式

做题点这里👉 : 洛谷1149
在这里插入图片描述

我们来实现一下:

import java.util.Scanner;
public class Main {static int[] match = new int[1000];static int[] arr = new int[1000];static int n = 0, cnt = 0;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();n -= 4;//等号和加号用的火柴棒match[0] = 6;match[1] = 2;match[2] = 5;match[3] = 5;match[4] = 4;match[5] = 5;match[6] = 6;match[7] = 3;match[8] = 7;match[9] = 6;//计算10以后的数字用到的火柴帮数量for (int i = 10; i < 1000; i++) {match[i] = match[i % 10] + match[i / 10];}dfs(1, 0);System.out.println(cnt);}public static void dfs(int x, int sum) {//超过给出的数量,剪枝if (sum > n) {return;}if (x > 3) {if (sum == n && arr[1] + arr[2] == arr[3]) {cnt++;}return;}for (int i = 0; i < 1000; i++) {arr[x] = i;dfs(x + 1, sum + match[i]);arr[x] = 0;}}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spark任务,使用 repartition 对数据进行了重新分区,但任务输入数据大小仍存在不均衡

目录 目录 确认 Spark 任务重新分区后的数据不均衡 1. 检查分区大小 2. 使用 DataFrame API 检查分区 3. 使用 Spark UI 查看分区情况 4. 使用日志记录分区信息 可能原因 1. 数据分布不均衡 2. 分区策略 3. 数据预处理 解决方案 1. 检查数据分布 2. 使用 coalesce…

代码随想录算法训练营Day62|冗余连接、冗余连接II

冗余连接 108. 冗余连接 (kamacoder.com) 考虑使用并查集&#xff0c;逐次将s、t加入并查集中&#xff0c;当发现并查集中find(u)和find(v)相同时&#xff0c;输出u和v&#xff0c;表示删除的边即可。 #include <iostream> #include <vector> using namespace s…

【分布式系统】注册中心Zookeeper

目录 一.Zookkeeper 概述 1.Zookkeeper 定义 2.Zookkeeper 工作机制 3.Zookkeeper 特点 4.Zookkeeper 数据结构 5.Zookkeeper 应用场景 统一命名服务 统一配置管理 统一集群管理 服务器动态上下线 软负载均衡 6.Zookkeeper 选举机制 第一次启动选举机制 非第一次…

uboot镜像之boot烧写

适用场景:单板上没有boot&#xff0c;和按地址烧写配合&#xff0c;可完成单板所有镜像的烧写。 原理:bootrom读取u-boot.bin至内存并执行此u-boot 然后运行uboot,通过uboot sf命令烧录uboot.bin到flash存储空间去. bootrom读取u-boot.bin至内存并执行此u-boot 通过uboot sf命…

【开发工具】webStrom2024版-插件

Chinese (Simplified) Language Pack / 中文语言包 英文界面变为中文界面。 Material Theme UI 界面美化 Rainbow Brackets 代码量变大时&#xff0c;总会伴随着各种括号&#xff1b;它能将括号赋予不同的颜色&#xff0c;方便我们区分。 Translation 中英互译&#xff0c;…

Codeforces Round 918 (Div. 4)(A~F)

目录 A. Odd One Out B. Not Quite Latin Square C. Can I Square? D. Unnatural Language Processing E. Romantic Glasses F. Greetings A. Odd One Out Problem - A - Codeforces 输出一个不同于其他两个数的数&#xff0c;用异或操作可以轻松解决。 void solve{int…

高考志愿填报,选专业是看兴趣还是看就业?

对于结束高考的学生来说&#xff0c;选择专业的确是一个非常让人头疼的事情。因为很多人都不知道&#xff0c;选专业的时候究竟是应该看一下个人兴趣&#xff0c;还是看未来的就业方向&#xff0c;这也是让不少人都相当纠结的问题。这里分析一下关于专业选择的问题&#xff0c;…

windows下docker安装

目录 前言 1.搭建WSL2环境 1.1打开控制面板 1.2 将WSL 2设置为默认值 2.安装docker 3.使用docker 结论&#xff1a; 前言 本文安装docker&#xff0c;将使用WSL2&#xff0c;而不是vmeare和Hyper-V&#xff0c;也不需要你另外安装Linux系统&#xff08;如Ubuntu&#x…

PostgreSQL主从同步

目录 一、主从复制原理 二、配置主数据库 2.1 创建同步账号 2.2 配置同步账号访问控制 2.3 设置同步参数 3.4 重启主数据库 三、配置从数据库 3.1 停止从库 3.2 清空从库数据文件 3.3 拉取主库数据文件 3.4 配置从库同步参数 3.5 启动从库 四、测试主从 4.1在主库…

【HTML入门】第四课 - 换行、分割横线和html的注释

这一小节&#xff0c;我们继续说HTML的入门知识&#xff0c;包括换行、横线分割以及注释&#xff08;html的注释&#xff09;。 目录 1 换行 2 分割横线 3 html注释 1 换行 html中分为块元素和行内元素。这一小节呢&#xff0c;先不说这些元素们&#xff0c;我们先说一下换…

免费分享:1981-2016全球粮食产量数据集(附下载方法)

了解主要作物的历史产量模式&#xff0c;包括趋势和年际变化&#xff0c;对于了解在粮食需求和气候变化日益增长的情况下粮食生产的现状、潜力和风险至关重要。 数据简介 1981-2016全球粮食产量数据集是农业普查统计&#xff08;粮农组织报告的国家产量统计数据&#xff09;和…

云渲染平台那个好?2024云渲染测评

1.渲染100&#xff08;强烈推荐&#xff09; 以高性价比著称&#xff0c;是预算有限的小伙伴首选。 15分钟0.2,60分钟内0.8;注册填邀请码【5858】可领30元礼包和免费渲染券) 提供了多种机器配置选择(可以自行匹配环境)最高256G大内存机器&#xff0c;满足不同用户需求。支持…

GDBFuzz:基于硬件断点的嵌入式系统模糊测试工具

关于GDBFuzz GDBFuzz是一款功能强大的模糊测试工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以使用硬件断点对嵌入式系统进行模糊测试。 GDBFuzz的理念是利用微控制器的硬件断点作为覆盖引导模糊测试的反馈。因此&#xff0c;GDB被用作通用接口以实现广泛的适用…

Java异常详解及自定义异常

认识异常&#xff0c;掌握异常处理主要的5个关键字&#xff1a;throw、try、catch、final、throws并掌握自定义异常 目录 1、异常概念与体系结构 1、1异常的概念 1、2异常体系结构 1、3异常的分类 编译时异常&#xff1a; 运行时异常 &#xff1a; 2、异常处理 2、1防御式…

如何确保 PostgreSQL 在高并发写操作场景下的数据完整性?

文章目录 一、理解数据完整性二、高并发写操作带来的挑战三、解决方案&#xff08;一&#xff09;使用合适的事务隔离级别&#xff08;二&#xff09;使用合适的锁机制&#xff08;三&#xff09;处理死锁&#xff08;四&#xff09;使用索引和约束&#xff08;五&#xff09;批…

【C++】类和对象(中)--下篇

个人主页~ 类和对象上 类和对象中-上篇 类和对象 五、赋值运算符重载1、运算符重载2、赋值运算符重载3、前置和后置重载 六、const成员七、日期类的实现Date.hDate.cpptest.cpptest1测试结果test2测试结果test3测试结果test4测试结果test5测试结果test6测试结果test7测试结果 八…

SAP FICO自定义权限对象及自定义作业创建

设置的通用说明 要求设置税收分组权限&#xff0c;自定义权限对象&#xff1a;Z_SSFZ 执行按钮权限控制&#xff1a;权限对象Z_SSFZ 字段名&#xff1a;ZSSFZ对应维护税收分组 字段名&#xff1a;ZACTVT01 01&#xff1a;付款银行信息维护 02&#xff1a;员工基本信息维护…

知识图谱驱动的深度推理:ToG算法的创新与应用

LLMs通过预训练技术在大量文本语料库上生成连贯且符合上下文的响应。然而&#xff0c;面对需要复杂知识推理的任务时&#xff0c;它们存在明显的局限性。这些问题包括对超出预训练阶段的专业知识的准确回答失败&#xff0c;以及缺乏责任性、可解释性和透明度。为了解决这些问题…

伦敦银交易平台价格的突破成不成功?这点很重要!

在伦敦银交易中&#xff0c;当银价出现突破的时候&#xff0c;也正是引起很多投资者关注的时候。一旦银价出现突破&#xff0c;很可能是新行情的开端。但是做过突破交易&#xff0c;有相关经验的朋友会发现&#xff0c;自己在伦敦银交易平台做突破的时候&#xff0c;也并不是每…

【QT】多元素控件

多元素控件 多元素控件1. List Widget2. Table Widget3. Tree Widget 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别&#xff0c;以 QTableWidget 和 QTableView 为例&#xff1a; …