【刷题】备战蓝桥杯 — dfs 算法

news2024/10/7 18:22:42

在这里插入图片描述
送给大家一句话:

风度真美!
即使流泪,也要鼓掌,
即使失望,也要满怀希望。
——刘宝增

dfs 算法

  • 1 前言
  • 2 洛谷 P1030 [NOIP2001 普及组] 求先序排列
    • 题目描述
    • 算法思路
  • 3 洛谷 P1294 高手去散步
    • 题目描述
    • 算法思路
  • 4 蓝桥真题 十三届省赛 飞机降落
    • 题目描述
    • 算法思路
  • 5 总结
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 前言

在蓝桥杯的比赛中,深度优先搜索(DFS,Depth-First Search)算法是一种常用的搜索算法,它通过尽可能深地搜索树的分支,来寻找解决方案。由于其简单和易于实现的特性,DFS成为解决问题的强大工具,尤其是在数据规模较小的情况下。数据在100以内一般使用dfs

运行原理: DFS算法的核心思想是从一个起点开始,沿着树的边走到尽可能深的分支上,然后回溯到之前的分叉点,寻找未探索的分支。这个过程重复进行,直到找到解决方案或探索完所有可能的路径。DFS通常使用递归实现,这使得代码简洁易读。它利用栈(递归调用栈)来记录访问路径,从而实现回溯功能。基本蓝桥杯dfs算法题型可以使用以下模版:

#include <bits/stdc++.h>
//视情况而定
#define int long long 
#define endl '\n'
#define N 1001

using namespace std;
//往往需要一个哈希表来辅助判断
int vis[N] = {0};
void dfs()
{
	//退出条件很重要!!!
	if() return ;
	for()
	{
		//跟新结果
		//继续深入
		dfs();
		//回溯
	}
}

signed main()
{
	//加快读写速度 也可以直接使用C语言标准输入输出函数
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	//多组数据
	int t = 0; cin >> t;
	while(t--)
	{
		//进行基础读入数据
		//构建图 ,记录结构体等
		//解决问题
		dfs();
	}
	return 0}

常用于以下题型:

  1. 路径问题: 寻找从起点到终点的路径,或者求解所有可能的路径。
  2. 排列组合问题: 需要枚举出所有可能的情况时,如全排列、组合选择。
  3. 连通性问题: 如判断图中两个节点是否连通,或者求解连通分量。
  4. 解谜与回溯问题: 如N 皇后问题、迷宫探索、数独解题等。

注意事项:

  • 栈溢出问题(一般不用考虑): 由于DFS使用递归实现,深度过大时可能会导致栈溢出。针对这一点,可以尝试使用迭代深化搜索(IDS)或非递归方式实现DFS。
  • 重复状态处理(一定要仔细): 在搜索过程中可能会遇到重复状态,如果不加以处理,可能会导致算法陷入无限循环。通常使用访问标记(如访问数组)来避免重复访问。
  • 选择合适的剪枝策略(尽力而行): 合理的剪枝可以显著提高DFS的效率。通过预先判断某些路径是否可能达到目标,从而避免无效搜索。

通过以上的解析,我们可以看到DFS不仅在蓝桥杯中,在很多算法竞赛和实际问题解决中都是一个非常实用的工具。它虽然在处理大数据量时可能会遇到性能瓶颈,但在数据量适中、需要深度搜索解决方案的问题上,DFS仍然是一个十分可靠的选择。
下面我们来一起做几道竞赛题目来试试手!

2 洛谷 P1030 [NOIP2001 普及组] 求先序排列

题目描述

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数 ≤8)

输入格式
共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
输出格式
共一行一个字符串,表示一棵二叉树的先序。

根据题目,我们需要通过二叉树的中序遍历和后序遍历来写出前序遍历的结果。对于二叉树的确定单凭中序遍历或者后序遍历是不可能的,只有两者结合才能确定一棵完整的二叉树!来看样例:

  • 输入
    BADC
    BDCA
  • 输出
    ABCD

我们可以画出树的结构:
在这里插入图片描述
这样前序遍历的结果就有了

算法思路

这道题涉及了二叉树,那么如果不使用dfs 就会非常复杂捏!所以我们把解题交给dfs,重重递归解决问题:

  1. 首先通过后序遍历 , 我们可以确定根节点 (输出打印)
  2. 通过在中序遍历中找到根节点的位置,可以区分左右子树
  3. 区分出左右子树后,就可以继续寻找左右子树的根节点 ,重复1 - 2操作即可。
#include<iostream>
#include<string>

#define int long long 

using namespace std;

void dfs(string in ,string af)
{
	//为空时直接退出
	if(in.size() <= 0) return ; 
	//通过后序遍历获取根节点 
	char root = af[af.size() - 1];
	//输出
	cout << root;
	//分割左右子树 先变量左子树 再遍历右子树 
	int pos = in.find(root);  
	//注意substr的使用方法 (pos,len)从pos位置开始遍历len个数据
	dfs(in.substr(0 , pos), af.substr(0 , pos));
	dfs(in.substr(pos + 1 , in.size() - 1 - pos), af.substr(pos , af.size() - 1 - pos)) ;
}

signed main()
{
	//加快读写速度 也可以直接使用C语言标准输入输出函数
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	//通过string 来记录遍历的数据 比较方便(速度比较慢 数据量较小时问题不大)
	string in , af;
	cin >> in;
	cin >> af;
	//开始解决
	dfs(in , af);	
} 

其中我们使用了string来记录遍历的数据 ,这样使用起来比较方便,但是速度比较慢(数据量较小时问题不大)。
然后通过不断寻找根节点,打印根节点来完成前序遍历。

提交!全部AC!!!


3 洛谷 P1294 高手去散步

链接:高手去散步

题目描述

鳌头山上有 n 个观景点,观景点两两之间有游步道共 m 条。高手的那个它,不喜欢太刺激的过程,因此那些没有路的观景点高手是不会选择去的。另外,她也不喜欢去同一个观景点一次以上。而高手想让他们在一起的路程最长(观景时它不会理高手),已知高手的穿梭机可以让他们在任意一个观景点出发,也在任意一个观景点结束。

输入格式
第一行,两个用空格隔开的整数 n 、 m 之后 m 行,为每条游步道的信息:两端观景点编号、长度。
输出格式
一个整数,表示他们最长相伴的路程。

根据题目描述,我们需要在一张地图中寻找最长的行走路线,直接使用暴力dfs是一种非常好的办法。

算法思路

这里需要对地图进行记录,相比直接的图标来记录(一个一个节点的地图)矩阵来记录地图更加方便,这里就是线性代数的美丽世界。通过 n x n的二维数据模拟矩阵,记录从一个节点前往另一个节点的距离,是不是非常方便:

  1 2 3 4                              
1 0 0 0 0 
2 0 0 0 0  
3 0 0 0 0 
4 0 0 0 0       

这样的一张矩阵既可以记录4个景点之间是否有道路,并储存道路距离。

有了这张表,接下来的思路就简单了

  1. 首先先读入地图
  2. 让遍历所有的景点(因为出发点可以是任意一个),并进行最长路程计算
  3. 进行最长路程计算的过程是
    • 通过选取地图找到还没有去过的景点,并更新距离,直到没有路为止
    • 开始回溯,保证所以路线均被走过
#include<iostream>
#include<cmath>
#include<cstring>

using namespace std;
//n 个景点  m 条路 
int n , m;
//用来判断是否去过 
int hash1[20 + 20] = {0};
//地图矩阵 (+20 为了防止溢出)
int map[20 + 20][20 + 20]; 
//答案
int maxpath = 0; 

void dfs(int i , int dis)
{
	//对每一条路径进行搜索 
	for(int j = 1 ; j <= n ; j++)
	{
		//存在道路 并且 没去过 进行搜索 
		if(map[i][j] && hash1[j] == 0)
		{
			//更新结果 
			hash1[j] = 1;
			//搜索 
			dfs(j , dis + map[i][j]);
			//回溯 这个很重要!!!
			hash1[j] = 0; 
		} 
		//不存在道路和没去过的景点 说明走完了 更新结果 取最大值
		else{
			maxpath = max(maxpath , dis); 
		}
	}
} 

signed main()
{
	//加快读写速度 也可以直接使用C语言标准输入输出函数
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	//创建矩阵 
	cin >> n >> m;
	while(m--)
	{
		//两个景点编号 间隔距离 
		int v1 ,v2 , dis;
		cin >> v1 >> v2 >> dis;
		//储存到地图中 
		map[v1][v2] = dis;
		map[v2][v1] = dis;
	}
	
	//对每个位置进行深度优先搜索
	for(int i = 1 ; i <= n ;i++)
	{
		int dis = 0;
		//记录来过 
		hash1[i] = 1;
		//怕什么 搜! 
		dfs(i , dis) ;
		//回溯 这个很重要!!!
		hash1[i] = 0; 
		//哈希表归零
		memset(hash1 , 0 ,sizeof(hash1));
	}
	cout << maxpath;
	return 0;
}

代码很好理解,就是把所有可能的路线全部遍历一遍,找到最合适的答案!!!

提交:全部AC!!!

4 蓝桥真题 十三届省赛 飞机降落

链接:飞机降落

题目描述

N架飞机准备降落到某个只有一条跑道的机场。其中第架飞机在T时刻到达机场上空,到达时它的剩余油料还可以继续盘旋D个单位时间,即它最早可以于T时刻开始降落,最晚可以于T+D时刻开始降落。降落过程需要L个单位时间。-架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。请你判断N架飞机是否可以全部安全降落。

输入格式
输入包含多组数据。第一行包含一个整数T,代表测试数据的组数。对于每组数据,第一行包含一个整数N。以下N行,每行包含三个整数:T,D和L。
输出格式
对于每组数据,输出YES或者NO,代表是否可以全部安全降落。

样例输入

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

样例输出

YES
NO

样例说明
对于第一组数据,可以安排第3架飞机于0时刻开始降落,20时刻完成降落。安排第2 架飞机于 20时刻开始降落,30时刻完成降落。安排第1架飞机于30时刻开始降落,40时刻完成降落。
对于第二组数据,无论如何安排,都会有飞机不能及时降落。

算法思路

这道题通过数据范围,我们应该想到dfs算法,那么应该如何解呢???我们需要一个飞机结构体来记录相应信息,一个哈希表来记录飞机状况。然后获取数据, 接下来就要进行深度优先搜索了:

  1. 寻找可以降落的飞机(并标记为已经降落),更新时间
  2. 再次寻找可以降落的飞机
  3. 如果全部降落,那么返回true

这里的更新时间很有说法:

  1. 首先每次的dfs都会有一个现在的时间刻
  2. 我们需要判断飞机到达的时间刻加上可以盘旋的时间是否大于当前时间刻,如果满足那么就可以进行降落
  3. 需要更新的时间 是 开始降落的时间加上降落所需时间
  4. 开始降落的时间是dfs目前的时间刻与飞机到达时间的较大值,因为一定要等到上一架飞机降落后并且当前飞机可以降落才能进行。

这样我们就可以写出基本的思路:

#include<bits/stdc++.h>

using namespace std;

#define N 30

//用来判断是否已经降落
int hash1[N] = {0};
struct plane
{
  //T时刻到达 可以盘旋D时间   降落需要L时间
  int T , D , L;
}P[N];

bool flag = false; // 判断是否成功

void dfs(int n , int cnt , int time)
{
  //已经降落的飞机数量等于总数,那么就成功
  if(n == cnt) 
  {
    flag = true;
    return ;
  }
  else
  {
    //遍历所有飞机
    for(int i = 0 ; i < n ;i++)
    {
      //如果该飞机没有降落 并且 满足可以降落的条件
      if(hash1[i] == 0 && P[i].T + P[i].D  >= time)
      {
        hash1[i] = 1; //降落
        //继续深度搜索 更新时间!!!
        dfs(n, cnt + 1 , max(time , P[i].T) + P[i].L );
        hash1[i] = 0; //回溯
      }
    }
  }
}
signed main()
{
  //加快读写速度 也可以直接使用C语言标准输入输出函数
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
  // t 组数据
  int t = 0; cin >> t;
  while(t--)
  {
    int n = 0; cin >> n; 
    int m = n; int i = 0;
    //读入飞机数据
    while(m--)
    {
      cin >> P[i].T >> P[i].D >> P[i].L;
      i++;
    }
    //开始遍历
    dfs(n , 0 , 0);

    if(flag) cout << "YES\n";
    else cout << "NO\n";
    flag = false;
    memset(hash1 , 0 , sizeof(hash1));
  }
  return 0;
}

我们提交:通过所以测试点!!!

5 总结

  1. dfs算法在数据较小的情况下可以使用。
  2. 一定一定要确定好终止条件,避免栈溢出。
  3. 相应做好回溯,保证每次的遍历都是不一样的选择,避免少结果。
  4. 针对题目进行对应细节处理,有能力的话可以进行剪枝优化!!!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

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

相关文章

骨传导耳机哪个牌子好?热销榜TOP5机型大揭秘!

市场上的骨传导耳机品质参差不齐&#xff0c;大家在挑选产品时一定要注意&#xff0c;避免入手到劣质产品&#xff0c;要知道&#xff0c;这些劣质产品不仅仅是使用体验差&#xff0c;并且长时间使用还容易损伤身体健康&#xff0c;那么面对市面上如此多的骨传导耳机品牌&#…

【Java核心能力】编程功底-设计模式之策略模式

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

最长公共子序列(线性dp)-java

本文主要来描述两个字符串的最长公共子序列问题 文章目录 前言 一、最长公共子序列 二、算法思路 1.dp[i][j]的四种情况 2. dp[i-1][j]、dp[i][j-1]、dp[i-1][j-1]的关系 3.dp数组的状态转移方程 4.dp数组具体如下 三、代码如下 1.代码如下&#xff08;示例&#xff09;&#x…

Linux shell编程学习笔记46:awk命令的由来、功能、格式、选项说明、版权、版本

0 前言 在编写Linux Shell脚本的过程中&#xff0c;我们经常要对Linux命令执行的结果进行分析和提取&#xff0c;Linux也在文本分析和提取这方面提供了不少的命令。比如我们之前研究过的cut命令。 Linux shell编程学习笔记43&#xff1a;cut命令https://blog.csdn.net/Purple…

【LeetCode】手撕系列—82. 删除排序链表中的重复元素 II

目录 1- 思路2- 题解⭐删除排序链表中的重复元素 II——题解思路 3- ACM模式 原题链接&#xff1a;82. 删除排序链表中的重复元素 II 1- 思路 定义虚拟头结点 定义 cur 指针&#xff0c;cur指针始终指向虚拟头结点&#xff0c;依此操作 cur.next 和 cur.next.next while条件为…

无线游戏手柄的测试(Windows11系统手柄调试方法)

实物 1、把游戏手柄的无线接收器插入到电脑usb接口中 2、【控制面板】----【查看设备和打印机】 3、【蓝牙和其它设备】--【更多设备和打印机设置】 4、鼠标右键【游戏控制器设置】 5、【属性】 6、【测试】&#xff08;每个按键是否正常&#xff09; 7、【校准】&#xff08;…

微信自动回复这样设置,让你的沟通更加高效便捷!

面对繁忙的生活和不断涌入的信息&#xff0c;我们往往无法及时回复微信好友的消息&#xff0c;这给沟通带来了一定的困扰。有没有一种方法能够让我们在忙碌的同时&#xff0c;依然能够及时回复好友并提高沟通效率呢&#xff1f; 答案是肯定的&#xff01;我们可以通过微信管理…

智能传真机触摸屏中应用的触摸感应芯片

智能传真机是应用扫描和光电变换技术&#xff0c;把文件、图表、照片等静止图像转换成电信号&#xff0c;传送到接收端&#xff0c;以记录形式进行复制的通信设备。智能传真机将需发送的原件按照规定的顺序&#xff0c;通过光学扫描系统分解成许多微小单元&#xff08;称为像素…

配置交换机 SSH 管理和端口安全

实验1:配置交换机基本安全和 SSH管理 1、实验目的 通过本实验可以掌握&#xff1a; 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 2、实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 3、实验步骤 &#xff08;1&#xff09;配置交换机S1 Swit…

Linux:文本编辑器 - vim

Linux&#xff1a;文本编辑器 - vim vim基本操作普通模式模式切换移动光标复制粘贴删除替换撤销 底行模式行号查找 vim基本操作 Vim(Vi Improved)是一款功能强大的文本编辑器&#xff0c;是Unix/Linux系统中广泛使用的编辑器之一。它源于上世纪70年代开发的Vi编辑器&#xff0…

初识--数据结构

什么是数据结构&#xff1f;我们为什么要学习数据结构呢....一系列的问题就促使我们不得不了解数据结构。我们不禁要问了&#xff0c;学习C语言不就够了吗&#xff1f;为什么还要学习数据结构呢&#xff1f;这是因为&#xff1a;数据结构能够解决C语言解决不了的问题&#xff0…

数据结构和算法:回溯

回溯算法 回溯算法&#xff08;backtracking algorithm&#xff09;是一种通过穷举来解决问题的方法&#xff0c;它的核心思想是从一个初始状态出发&#xff0c;暴力搜索所有可能的解决方案&#xff0c;当遇到正确的解则将其记录&#xff0c;直到找到解或者尝试了所有可能的选…

spring-cloud微服务openfeign

Spring Cloud openfeign对Feign进行了增强&#xff0c;使其支持Spring MVC注解&#xff0c;另外还整合了Ribbon和Nacos&#xff0c;从而使得Feign的使用更加方便 优势&#xff0c;openfeign可以做到使用HTTP请求远程服务时就像洞用本地方法一样的体验&#xff0c;开发者完全感…

unity学习(83)——细节名称和血条

眼中有细节&#xff0c;学习的过程才能平稳&#xff01; 1.游戏更新时把名字也更新 代码如下&#xff1a; 效果如下&#xff1a; 2.因为是第三人称&#xff0c;从背后看&#xff0c;所以名称应该水平对称&#xff0c;翻转一下&#xff01;rotate y180 游戏内效果如下&#xf…

IEC101、IEC103、IEC104、Modbus报文解析工具

一、概述 国际电工委员会第57技术委员会&#xff08;IEC TC57&#xff09;1995年出版IEC 60870-5-101后&#xff0c;得到了广泛的应用。为适应网络传输&#xff0c;2000年IEC TC57又出版了IEC 60870-5-104&#xff1a;2000《远东设备及系统 第5-104部分&#xff1a;传输规约-采…

【数据交换格式】网络socket编程温度采集智能存储与上报项目技术------JSON、TLV

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

数据结构__顺序表和单链表

顺序表的改进 问题&#xff1a; 1. 中间/头部的插入删除&#xff0c;时间复杂度为O(N) 2. 增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间。会有不小的消耗。 3. 增容一般是呈2倍的增长&#xff0c;势必会有一定的空间浪费。例如当前容量为100&#xff0c;满了…

安卓逆向 | 某X游戏垂类Web nonce

*本案例仅做分析参考,如有侵权请联系删除 1.逻辑分析 通过XHR断点,然后逐步往上调发现nonce生出处。 在console执行下函数 其中 i,是当前日期和时间的秒级时间戳,并将其向下取整到最接近的整数。 i = ~~(+_.w() / 1e3)w</

CLI举例:上下行连接路由器(路由引流)

CLI举例&#xff1a;上下行连接路由器&#xff08;路由引流&#xff09; 介绍了集群设备&#xff0c;上下行连接路由器的配置举例。 组网需求 如图1所示&#xff0c;上行网络使用BGP&#xff0c;下行网络使用OSPF&#xff0c;多数据中心统一通过路由器R4接入Internet。 希望…

C++位图和布隆过滤器(含哈希切割)

文章目录 C位图和布隆过滤器&#xff08;含哈希切割&#xff09;1、位图&#xff08;Bitmap&#xff09;1.1、位图的概念1.2、位图的使用1.3、位图的模拟实现1.4、位图相关面试题 2、布隆过滤器&#xff08;Bloom Filter&#xff09;2.1、布隆过滤器的概念2.2、布隆过滤器的插入…