hello小伙伴们,我们又见面啦~上一篇文章蓝桥杯算法竞赛备考(一)—— 搜索专题(上)(C++)得到了小伙伴们的认可,我真的是喜出望外,小伙伴们的支持是我最大的动力噢~
好啦废话不多说,让我们进入今天的算法学习吧~
今天讲的主要有以下几点内容噢!
广度优先搜索的基本思想BFS的模板BFS经典例题记忆化搜索
文章目录
写在前面一、广度优先搜索(BFS)二、用队列来实现BFS(模板)三、BFS经典例题
1、模板题——迷宫最短路2、马的遍历3、块的个数 四、记忆化搜索
斐波那契数列(三种求解方法) 写在后面
一、广度优先搜索(BFS)
深度优先搜索会优先考虑搜索的深度,就是找不到答案不回头。 当答案在解答树中比较稀疏的时候,DFS会陷入过深的情况,搜索很多无效状态,一时半会找不到解,这时候我们就可以用BFS啦~在连通性,最短路问题中常优先考虑BFS。
BFS和DFS搜索的顺序是不一样的。DFS前面已经讲到过,是以深度为第一关键词的,也就是说从某个结点出发总是先选择其中一个分支前进,而不管其他的分支,直到碰到死胡同才停止。然后回溯到上一个结点选择其他的分支。
而BFS搜索的顺序是这样的,从某个结点出发,总是先依次访问这个结点能直接到达的所有结点,然后再访问这些结点所能直接到达的所有结点,依次类推,直到所有的结点都访问完毕。
下面呢我们看一个迷宫,小伙伴们可能会对BFS有一个清晰的认识。
BFS搜索的顺序是这个样子滴~首先从起点A出发,它能到达的所有结点有B和C(第二层),因此接下来去访问B,B能直接到达D和E(第三层),接下来访问C,C能直接到达F和G(第三层)。第二层访问结束,接下来访问D,D能直接到达I和G和H(第四层)。接下来访问E,E能直接到达M和L和K(第四层),接着访问F,F是死胡同。接下来访问G,G是终点,搜索结束。
至于那些未访问过的结点,我们就不用去管它了。
可以看出层数就是从结点A出发到达其他结点的最小步数。广度优先搜索会优先考虑每种状一个专题态的和初始状态的距离,也就是与初始状态越接近的就会越先考虑。这就能保证一个状态被访问时一定是采用的最短路径。
其实DFS和BFS都可以从树的角度去分析。
DFS相当于树的先序遍历,BFS相当于树的层次遍历。
DFS搜索顺序是123456 ,BFS搜索顺序是125346。
相信小伙伴们很容易就能看出来~
二、用队列来实现BFS(模板)
BFS的搜索过程很像一个队列,队列的特点就是先进先出。谁入队早就先访问谁。
我们简单的来分析一下队列模拟的过程~
先把起点A入队,取出队首A,将A直接到达的B和C入队。队列元素有{B,C}。再将队首B取出,将B直接到达的D和E入队。此时队列元素有{C,D,E}。再将队首C出队,将C直接到达的F和G入队。此时队列元素有{D,E,F,G}。再将队首D出队,将D直接到达的I和J和H入队。此时队列元素有{E,F,G,I,J,H}。再将队首E出队,将E直接到达的M和L和K入队。此时队列元素有{F,G,I,J,H,M,L,K。再将队首F出队,F没有直接相连的新节点,此时队列元素有{G,I,J,H,M,L,K}。再将G出队,找到终点,算法结束。
下面给出BFS的模板。
还是那句话多记些模板对比赛是很有帮助的噢~
void bfs(){queue
对模板做几点说明
建议大家用stl中的queue表示队列。操作方便。头文件要加#includetop = q.front()。
还有一个问题,我们在使用BFS时会设一个inq数组记录结点有没有入队,为什么不是记录结点有没有访问呢,不知道小伙伴们想没想过这个问题。
区别在于,设置成结点是否已访问,会导致有些点在队列中但是还没有被访问,由于其他结点可以到达它而将这个结点入队,导致很多结点重复入队,这是很不明智的。
以后也会专门出一篇讲解比赛中常用的STL标准模板库。
上面对BFS的讲解参考胡凡《算法笔记》并补充了自己对BFS的理解。
三、BFS经典例题 1、模板题——迷宫最短路
题目分析
该题是要求从起点到终点的最小步数。我们可以有两种解决方案,DFS和BFS。
可以DFS所有从起点到终点的所有路径,然后取路径最短的那条。BFS是很理所当然的,因为求的是最小步数,涉及最短问题。
我们先来看一下BFS该如何解决这个问题。因为是板子题嘛直接照着模板敲就好了~
AC代码(BFS)
//bfs 广度优先搜索//迷宫问题 求解最短路径#include
我们再来看一下如何用DFS来解决这个问题。用DFS搜索出所有可以从起点到终点的路径,并记录步数。DFS函数记录当前搜索的点的坐标(x,y)和所用步数step。用一个变量minsize更新最小步数minsize = min(minsize, step)。其余的操作其实和上一篇文章的迷宫问题求方案数是差不多的啦。小伙伴们还是不懂的话可以去看前一篇文章呀~
AC代码(DFS)
#include
对上述代码做几点说明
里面用到了STL中的pair。当想要将两个元素绑在一起作为一个元素时,又不想因此定义一个结构体,那么就可以用pair这个小玩意啦~pair里面有两个元素,类型是可以自定义的,分别是first,second。
*要注意minsize初始化要大一些,这样才能更新最小。
*DFS求解最短路问题是没有优势的。显而易见,DFS会搜索所有的路径,然后求出最小步数,搜索了很多无效状态,效率是非常低的,很容易就TLE。
2、马的遍历
题目分析
这道题其实和前面那一道很相似,前面那一道是求从起点到终点的最短步数,而这道是求从起点(马的位置)到各个点(棋盘各个点)的最短步数。
那么该怎么办呢~很简单,从马的位置点(x, y)开始BFS,用一个ans数组来记录起点到达各个点时的层数,也就是最短步数。最后输出ans即可。
题目本身的思路还是很好想滴~还是有些地方需要注意一下。
一个马可以走的位置已经用圆圈圈住啦~八个偏移量(-2,1),(-2,-1),(-1,2),(-1,-2),(2,1),(2,-1),(1,2),(1,-2)。我们可以用方向数组来表示:
int dx[8]={-2,-2,-1,-1,2,2,1,1};int dy[8]={1,-1,2,-2,1,-1,2,-2}; 输出格式要求左对齐,宽5格。
printf("%-5d", ans[i][j]);cout< 第二个cout的写法大家可以试一试可不可以通过,这是我在洛谷题解里面找到的。还是推荐第一种printf的写法。 像一些方向数组,初始化数组等一些小技巧小伙伴们要好好整理一下噢,可以提高代码的效率和简洁性。 AC代码 #include 当然为了节省空间这道题也可以只开一个ans数组。来记录最少步数和是否已入队。 ans[x][y] != -1说明(x,y)点已入队;ans[x][y] == -1说明(x,y)点未入队。 这道题貌似有个名字叫连通块问题。 这道题难了我好长时间,我觉得有些小伙伴肯定也会在这道题上卡一下。主要是题目意思有点难理解。我开始的时候以为块是只十字架结构的1,也就是1的上下左右都是1,然后这些1构成的就是一个块。然后看样例发现块数不太对。样例是4块。 经过长时间的绞尽脑汁的琢磨(原谅我的菜),我才明白块说的是从某个1开始,看它的上下左右有没有1,有1的话就在看这些1的上下左右有没有1,依次类推,直到找不到1为止。这么说呢可能小伙伴们还是不太懂,那么我们举个例子吧~ 我们还可以发现单独的1也可以是一个块,不然这题答案就是3了。 AC代码 #include BFS的做法相对来说还是比较固定的,所以注释有些重复的简单的我就不再写了。 记忆化搜索的目的就是为了解决重复计算。 通过一个斐波那契数列和小伙伴们来聊一聊什么是记忆化搜索吧~ #include #include 记忆化搜索包含两个要素记忆化和搜索。 沿用搜索的一般模式,本质还是用递归函数实现。在搜索的过程中将已经得到的解保存起来,下次需要时直接用。 记忆化搜索是理解DP思想很重要的基础噢,把记忆化搜索掌握明白,会很容易就入门动态规划噢~ 还有一种递推的方法,时间复杂度只有 O ( n ) O(n) O(n)。递推和递归的区别在于,递推是自底向上,递归是自顶向下。 Answer3、递推法 #include 美好的时光总是短暂的,今天的算法学习就要告一段落了。搜索是蓝桥杯的基础中的基础,小伙伴们一定要好好学噢~。下篇文章初步计划是要讲下蓝桥杯涉及到搜索的真题,小伙伴们可以期待一下噢。 由于我水平有限可能一些地方讲的不清楚或者不对,都欢迎小伙伴们给我提建议噢,私信我看到的话一定会回的。
由于到达不了的地方要输出-1,所以我们要将ans数组初始化成-1。技巧memset(ans, -1,sizeof(ans));。
具体代码这里就不再赘述了噢~
3、块的个数
题目分析
样例中的四块是用圆圈圈住的这四个。以最上面的那个为例,从最左边的1开始搜索,它的右边有1个1。这个1的右边和下边有1。这些1的上下左右就没有新的1了。因此这些1组成一个块。说到这大家应该明白了许多hh~。
理解了题意,思路也就很好想了。我们可以BFS某个1,找出与它符合块条件的所有1,通过BFS将这些1标记下来,进而可以不重复的搜索块的个数。
四、记忆化搜索
大家可以尝试用DFS来做一下这道题噢~ 一道题用不同的解法才能看出算法的魅力!
DFS解答的代码我放在了这里噢~
斐波那契数列(三种求解方法)
Answer1、递归实现
斐波那契数列的递归实现是比较简单的。就抓住递归的边界和递归式就没问题了。
递归的时间复杂度是成指数级增长的,因此如果不进行一些优化的话,求解的N很大的话,很容易就TLE。我们可以看出fib(3)被重复计算了两次,如果我们将这个数保存起来,再遇到的时候就可以直接使用,大大简化了计算量。这就是记忆化搜索的思想。
Answer2、记忆化搜索
记忆化搜索按照自顶向下的顺序,每求得一个状态时,就把它的解保存下来,以后再次遇到这个状态时就不用重复求解了。
写在后面
蓝桥杯系列在省赛前我会一直更新下去的。什么?你还不知道省赛是什么时候,4月9就要比赛啦!小伙伴们也要抓紧时间学习啦。我们下一期再见吧~
小伙伴们一键三连支持一下吧,绝对不会让你们失望的。