【算法】蓝桥杯dfs深度优先搜索之凑算式总结
导航
本文 → 《【算法】蓝桥杯dfs深度优先搜索之凑算式总结》
相关文章 →《【算法】蓝桥杯dfs深度优先搜索之排列组合总结》
→《【算法】蓝桥杯dfs深度优先搜索之图连通总结》
前言
曾几何时这个词现在用正适合不过了。
曾几何时我还是对dfs算法一脸懵x的状态,虽说大二的时候学过数据结构,但是那一学期被我荒废了......详情等我毕业了再说吧。
还有四天就要考蓝桥杯了,我分析了近四年的蓝桥杯(B组)题型,分类纯属个人理解,并非官方宣布,如下表:
注:
以下DFS代表Depth First Search首字母缩写,即深度优先搜索,简称深搜
以下DP代表Dynamic Programming首字母缩写,即动态规划,没有简称 : )
分类 | 题号 | 总分 |
---|---|---|
水题 | 1(3分)、2(5分)、4(11分) | 19分 |
DFS/爆破 | 3(9分)、5(15分)、7(21分) | 45分 |
冒泡 (加法乘法) |
6(17分) | 17分 |
取余 (饮料换购) |
8(13分) | 13分 |
矩阵 | 9(25分) | 25分 |
DP | 10(31分) | 31分 |
分类 | 题号 | 总分 |
---|---|---|
递推 | 1(3分)、2(5分) | 8分 |
函数调用 | 4(11分) | 11分 |
DFS/爆破 |
3(9分)、5(13分)、 6(15分)、7(19分)、 8(21分) |
77分 |
DP | 9(23分) | 23分 |
数据结构 | 10(31分) | 31分 |
分类 | 题号 | 总分 |
---|---|---|
文件处理 | 1(5分)、3(13分) | 18分 |
DFS/爆破 | 2(11分)、4(17分) | 28分 |
水题 | 5(7分) | 7分 |
DP | 6(9分)、8(21分) | 30分 |
日期问题 | 7(19分) | 19分 |
二分问题 | 9(23分) | 23分 |
前缀和 | 10(25分) | 25分 |
分类 | 题号 | 总分 |
---|---|---|
水题 | 1(5分)、2(7分) | 12分 |
复数 | 3(13分) | 13分 |
排序 | 5(9分)、6(11分) | 20分 |
找规律 | 7(19分) | 19分 |
尺取法 | 8(21分) | 21分 |
DFS/爆破 | 9(23分) | 23分 |
DP | 4(17分)、10(25分) | 42分 |
可以看到每年蓝桥杯都会考察dfs类型的题,而且分值在23-77分之间不等,虽说分值成逐年下降的趋势,但怎么说也有20+分,蓝桥杯满分150分,普通人(我就是普通人中之一)总共能拿到的分数大概在80分左右,而DFS就包括在这80分之内,所以一定要掌握好。
以上是对题型及分值的分析,接下来分析一下为何蓝桥杯总要考dfs算法?首先看一则人物百科:
再来看一下“深度优先搜索”的百度词条:
还有一点值得提出,这位大神是中国政府外聘千人计划之一,目前好像在中国清北或港大其中一个学校任教。
蓝桥杯作为一个算法类竞赛,再退一步讲,怎么说也得给老前辈一个面子吧。
综上所述,蓝桥杯考dfs算法的概率胜过千足金含金量的比率!
正文
下文的大部分灵感均来自于【CSDN】梅森上校《JAVA版本:DFS算法题解两个例子(走迷宫和求排列组合数)》
下面会贴出蓝桥杯第六届B组省赛第3题,第5题;第七届B组省赛第3题,第8题;第八届B组省赛第2题,共5道题。
因为它们都是:凑算式
【第一道题】
【分析】
总共有8个不同的字,每个字都代表不同的数,申请一个一维数组a[8]存储这8个不同的数。
a[8]对应有下标index:0至7
所以dfs方法定义为
然后设计结束条件,就是index等于8,越界了,代表找够了8个数,尝试进行判断是否构成等式。
如果index没有越界,a[]数组没存够8个数,就进行搜索。
因为从0至9搜索,且每个数不能重复,所以每访问一个都需要标记一下,visited[i] 为 0 代表未访问,为 1 代表已访问。
通过数学分析,祥字不可能为0,否则无法进位,三字必为1,因为十进制进位只能进1,所以最终搜索代码如下:
【完整代码】
没看懂这道题?没关系。继续往下看。
【第二道题】
【分析】
这是一道填空题,基本代码已经给出来了,我只是借题发挥,讲解一下dfs算法。
上一题是8个数,我们设置了一个a[8],这道题是9个数,所以设置一个a[9]
同样需要一个index:0至8
这道题和上一道题的区别在于题目用了dfs()方法多了一个数组参数,如下
结束条件和上一题类似,当index为9时,越界。
如果index不为9,代表还没凑够9个数,深搜。上一题深搜之前是做标记,深搜之后重置标记,这一题类似,深搜之前交换,深搜之后重置交换,代码如下:
这道填空题需要注意的是,不要完全照抄以致于连 int 都抄上了,导致 t 被声明两次,报错。
【完整代码】
又没看懂?没关系。继续往下看。
【第三道题】
此题和第二道题类似,都是1-9九个数字,不过这里要注意的是,第二题用乘法代替了除法,这道题虽然说也可以改成乘法,但是会相对麻烦一点,干脆就直接用除法,一旦涉及到除法,就有可能产生浮点数,所以我们在声明数组的时候不能再声明为int了,必须声明为double。
index还是int的,取值范围为0至8
dfs方法定义如下:
结束条件就是index==9,越界
当index不够9的时候,进行深搜
【完整代码】
当然,这道题也可以像第一题“三羊献瑞”那样,使用递归之前标记法
【标记法完整代码】
对visited初始化的说明:
Java语言int型数组初始化时会自动赋值为0,但是我为了直观表达,所以选择了显式初始化。
今天已经深夜1:22,还有一个四平方和没有写,明天再写
我一定会回来的......
我又回来了,继续看题。
【第四道题】
通过上面的三道题,我想现在的dfs的套路应该已经有一些印象了吧。
因为是四平方,要存四个数,所以我们思考都不用思考,先整个a[4]
同理,需要一个index,0至3
因为前几道题的程序都不需要我们进行输入,这道题需要输入一个num,而且还需要进行等式判断,所以需要作为dfs()方法的一个参数,这样dfs()方法定义如下:
递归结束条件为index==4,越界,代表a[]数组存够4个数了,可以进行等式判断了,代码如下:
如果index不等于4,代表4个数还没找够,深搜。这里要注意3件事:
一是深搜的范围,最大范围是三个0和输入数字的开根号。
二是Java中Math.sqrt()方法返回值是double,最好转为int,让两个int进行比较。
三是递归之前要不要留下已访问的标记,或者交换?从给出的输入样例可以看出来,有0 0 1 2 、 0 2 2 2 这样的输出,所以说数字是可以重复使用的,那么就没必要进行标记或者交换了。这也是这道题和前三道题区别之一。
综上,搜索代码如下
【完整代码】
【等等,这道题还没完】
毕竟是一道23分的大题,尊重一下出题人嘛,怎么会让你这么轻松的拿分呢?
如果按照上面的代码,输入5,会输出下面的结果
显然,人家只要第一行。在赵壮同学的指点下,直接在输出下面加一句System.exit(0);就可以了。
【再等最后一下,真的】
虽然说上面的代码能够实现输出第一行,但是深搜毕竟是一种盲目的递归,进行大量无用的尝试,最终导致超时。我仅仅是拿这道题让大家更深入了的理解一下dfs这个算法。就上面的代码,我测试了一下 773535 这个输入,Eason的《好久不见》循环播放三遍了都还没跑出来,这要是提交了蓝桥比赛,那就GG了......
其实深搜大部分都可以用for循环暴力破解代替,这样可以减少一下盲目性,比如这道题的解法可以参考下面这篇文章:
【CSDN】有梦就不怕痛《蓝桥杯 四平方和》
Java版代码如下(运行飞一般的流畅):
好了,我已经逐个分析了四道题了,本来已经快要分析吐了,不想再分析了。但是后来想了想小王子里面那句话:“每个大人都曾经是个孩子,可又有几个人记得呢?”还有我国晋惠帝说的一句千古名言:“何不食肉糜?”
下面这道题就算是对“凑算式”这类题型的一个总结吧。
【第五道题】
有了上面四道题的经验,看到这种凑算式的题,上来想都不用想:
第一件事,就是设数组
第二件事,数字能否重复使用?能,略过这条;不能,设标记数组,或者交换(我个人偏向设标记数组)
第三件事,定义dfs()方法
第四件事,判断递归结束条件,通常是index越界,并进行等式判断
第五件事,还未凑齐数,深度优先搜索
第六件事,写main()方法
完事
【完整代码】
这道题有个坑就是要去重,因为题中说明“旋转,镜像后相同的算一种”,所以最后要count/6.这个纯靠经验了,有过旋转经验的就知道,每个数都有机会占在顶点上,所有数固定后,转动的话,等于每个数计算了3次,所以要除以3;对于镜像,同样是靠经验,如果你知道从镜子里看左右会对换的话,就会知道要除以2,总共除以6.
在没做这道题之前,我是不知道镜像是什么样的,因此我还特意拿个镜子照了照。
这篇文章就到此结束吧,我要开一个新坑了,明天应该能发布新文章:
如果看完这5道题还不会这种“凑算式”的题,请发QQ邮箱:424171723@qq.com 或 直接加我QQ,进行手把手教学。
【参考文章】
- 【CSDN】梅森上校《JAVA版本:DFS算法题解两个例子(走迷宫和求排列组合数)》
- 【CSDN】winycg《2015蓝桥杯 三羊献瑞(回溯法dfs)》
- 【CSDN】有梦就不怕痛《蓝桥杯 四平方和》
- 还有一些CSDN、博客园、简书.......由于浏览篇幅太多,所以一些未列在其中,表示抱歉