第十四届蓝桥杯C++B组个人题解——无FJ
本文章仅是个人题解,不能保证全对,但思路大抵就是那么个思路。今年的题感觉要比去年难了些,F想不到很好的判环方法,最后一题都不知道再说什么,图论苦手是这样的,不过其它倒是挺对我胃口。
但说实话线下在学校机房比,超级不适应啊!
(更新:编程题库 - C语言网 (dotcpp.com)这里有民间数据了,可以去测一测)
A、日期统计
问题描述
小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的范围之内。数组中的元素从左至右如下所示:
5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1 0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3
现在他想要从这个数组中寻找一些满足以下条件的子序列:
-
子序列的长度为 8;
-
这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且要求这个日期是 2023 年中的某一天的日期,例如 20230902,20231223。yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。
请你帮小蓝计算下按上述条件一共能找到多少个不同 的 2023 年的日期。对于相同的日期你只需要统计一次即可。
问题解析
答案:235
可以发现,到了第59个数字的时候,才第一次凑出2023的子序列,那么对于剩下的其它数字,可以用四个for循环来枚举mmdd,在判断dd(即天数)符不符合mm(月数),注意去重。
或者是可以直接枚举2023年的365天,即从20230101枚举到20231231为止,然后看能不能在字符串中找到这么个序列。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
B、01串的熵
问题描述
问题解析
答案:11027421
这题对我来说主要难点在于算log,说实话我之前真不知道有函数能直接算log,这题的log我是采用了浮点型二分的方法来算的。并且也测试了下S=100的情况发现是正确的。然后我们就枚举0的个数就好了,1的个数就是23333333-0的个数。
不过我这做法有两点要注意的:
- 没必要从0开始枚举0的个数,实际上debug几遍后就能发现大约在11000000左右的时候算出来的值就很接近题目的这个值了。
- 二分的精度要高,一开始我习惯性的设置精度为1e-6,结果咋都跑不出正确结果,后来折磨了半小时后才发现精度要开到1e-15左右才能出正确结果。哭死。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
C、冶炼金属
问题解析
可以看出,V可能的最大值直接就是:a/b了,没有问题。
重点是最小值,既然要转换率V最小,那就是最后浪费的尽可能的多。用a/(b+1),即算出a个普通金属如果想冶炼出b+1个特殊金属,转化率是多少。再逆过来判断一下,如果a可以通过这个转化率冶炼出b+1个金属,则V++。直到正好冶炼出b个为止,这样最后浪费的数量就是最多的了。
最后,对于所有的炼金情况,它们的最大值我们取最小,它们的最小值我们取最大(实际就是区间合并的过程)
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
D、飞机降落
问题解析
一看数据,N<=10,直接枚举全部的排列次序,然后都模拟一遍,看能不能平稳落地就行。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
E、接龙数列
问题描述
问题解析
经典的dp问题,送分了属于是。
让我们删除最少的数,使得剩下的序列是接龙序列,实际上就是让我们求最长接龙序列的长度len,答案就是:n-len
对于这种:求最长XXX子序列的问题,一般的状态设置就是:f[i]是以i结尾的xxx序列,长度为f[i]。
那么我们就设:f[i]是以数字i结尾的最长的接龙序列,i的取值是0到9(不含前导0嘛)
first是数字a的第一个数字,last是数字a的最后一个数字,则有状态转移方程:
f[last]=max(f[last,f[first]+1);
最后取所有的序列的最大长度即可。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
G、子串简写
问题描述
问题解析
又是经典的题。
先用一个数组存下来从小到大所有c2字符的下标。
然后对s的每一个c1字符,在数组里找他俩下标相减大于等于k+1的即可。这一步我们可以用二分来优化掉。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
H、整数删除
问题描述
问题解析
这一题我感觉我写复杂了,但是也暂时想不到更好的点子,一开始想的链表,但我讨厌指针这玩意,所以我换了个写法。
我用的是:线段树求数组最小值+双并查集。
用一个并查集fa来链接左边的数,另一个并查集fb来链接右边的数:(如图)
至于找数组最小值和修改数组的事情,就交给线段树就行了。
如果某个数被删除了,则在线段树中将对应位置修改成一个很大的值,不要让他影响我们的判断。
不会线段树的同学也可以看看我主页的文章,有教你如何掌握线段树(基础)。
(做法应该没啥问题,主要担心会不会写错代码+常数太大了导致tle。)
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
I、景区导游
问题描述
问题解析
首先,根据题目的解释来看,如果路线是:2->6->5->1,那就相当于求出sum=dis(2,6)+dis(6,5)+dis(5,1)。dis(x,y)表示点x到点y的时间。
至于跳过某点,比如跳过6点,那么就是:sum-dis(2,6)-dis(6,5)+dis(2,5).
这样对于每次跳过某点,我们并不用重新计算花费时间,只要把修改跟跳过的点有关的三条路径长度求出来,再在sum的基础上修改即可。
那么此时问题就变成了,如何快速的求从x点到y点的距离。
注意题目说的,这个景区的路线呈现树的样子。也就是说,这是让我们求在树中任意两点的距离:(样例如图)
由此可知,只要知道了两个点的最近公共祖先,就可以很快的求出在树中任意两点的距离。
现在,要解决的就是快速求出两个点的最近公共祖先了。
关于这个,我这里用的是倍增法求lca,具体的这里就不展开了,感兴趣的同学可以去自行学习一下。
代码
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include