PAT | 蓝桥 | LeetCode学习路径 & 刷题经验

你们要不要考虑也节省一下时间~

这份PDF题目叫做《PAT | 蓝桥 | LeetCode学习路径 & 刷题经验 by 柳婼》,希望可以帮助学习算法路上的小可爱们节约时间、少踩一些坑、多走一些捷径~

一年前看过我blog的人应该知道,我曾经开通过知乎的值乎付费咨询,大概开通了大半年时间,期间也收到了很多咨询,绝大多数提问的话题都是“PAT、蓝桥、LeetCode怎么学如何刷题”,我也一一认真做了回答(绝大多数回答都在半小时以上),但是值乎的回答只能够发布语音,而且有回答时效,提问也有字数限制,后来问的人越来越多,我一天要花数小时在知乎回答上,而且回答的都是几乎相同的问题…对于分享算法经验来说,短短半小时确实不够,很多观点无法详细解释缘由,值乎一对一咨询确实不是一个很好的分享算法经验的平台,听者也很难短时间形成对问题答案的清晰理解,所以后来半年前我就关闭了知乎咨询的功能~不过还是有很多小可爱通过各种渠道向我咨询经验等问题,所以我花了好多天时间将这些问题的回答、刷题笔记、经验全都整理在了一份PDF中~这些问题包括:

里面不仅有关于这些竞赛的介绍、应该看哪些书、如何刷题,还有我自己刷算法过程中整理的笔记,目录如下:

这份经验一共3万7千字(想当年800字的高考作文都写那么辛苦…)在算法路上,我能帮的只有这么多了…希望能帮助你们少走很多弯路,少踩很多坑~剩下的就是靠你们自己刷题啦~还和离线版订阅获取的方式一样…打赏29元并备注邮箱号即可…打赏二维码在每篇博客文章点进去的最下面…24小时内发送到邮箱…(一般一两个小时就收到了~)时间仓促,后期还会对这份文档的内容进行扩充完善…到时候有版本更新还会继续发送到邮箱中~感谢支持与信任~

L3-029 还原文件 (30 分)-PAT 团体程序设计天梯赛 GPLT

一份重要文件被撕成两半,其中一半还被送进了碎纸机。我们将碎纸机里找到的纸条进行编号,如图 1 所示。然后根据断口的折线形状跟没有切碎的半张纸进行匹配,最后还原成图 2 的样子。要求你输出还原后纸条的正确拼接顺序。

输入格式:
输入首先在第一行中给出一个正整数 N(1<N≤10​5​​),为没有切碎的半张纸上断口折线角点的个数;随后一行给出从左到右 N 个折线角点的高度值(均为不超过 100 的非负整数)。

随后一行给出一个正整数 M(≤100),为碎纸机里的纸条数量。接下去有 M 行,其中第 i 行给出编号为 i(1≤i≤M)的纸条的断口信息,格式为:
K h[1] h[2] … h[K]
其中 K 是断口折线角点的个数(不超过 104 +1),后面是从左到右 K 个折线角点的高度值。为简单起见,这个“高度”跟没有切碎的半张纸上断口折线角点的高度是一致的。

输出格式:
在一行中输出还原后纸条的正确拼接顺序。纸条编号间以一个空格分隔,行首尾不得有多余空格。题目数据保证存在唯一解。

输入样例:
17
95 70 80 97 97 68 58 58 80 72 88 81 81 68 68 60 80
6
4 68 58 58 80
3 81 68 68
3 95 70 80
3 68 60 80
5 80 72 88 81 81
4 80 97 97 68

输出样例:
3 6 1 5 2 4

分析:h[i]存储第i个折线角点的高度,frag[i]存储第i个纸条碎片的信息。通过DFS从没有切碎的纸片最左端开始尝试所有纸条碎片的匹配,p表示当前所需要匹配的碎片左侧在未碎纸片上的位置,将匹配上的纸条碎片编号添加到Ans中记录,并使用vis标记是否已经匹配上了,如果Ans的容量等于纸片数量,则表示已经完成所有匹配,输出结果并返回~

L3-028 森森旅游 (30 分)-PAT 团体程序设计天梯赛 GPLT

好久没出去旅游啦!森森决定去 Z 省旅游一下。

Z 省有 n 座城市(从 1 到 n 编号)以及 m 条连接两座城市的有向旅行线路(例如自驾、长途汽车、火车、飞机、轮船等),每次经过一条旅行线路时都需要支付该线路的费用(但这个收费标准可能不止一种,例如车票跟机票一般不是一个价格)。

Z 省为了鼓励大家在省内多逛逛,推出了旅游金计划:在 i 号城市可以用 1 元现金兑换 ai 元旅游金(只要现金足够,可以无限次兑换)。城市间的交通即可以使用现金支付路费,也可以用旅游金支付。具体来说,当通过第 j 条旅行线路时,可以用 cj 元现金或 dj 元旅游金支付路费。注意: 每次只能选择一种支付方式,不可同时使用现金和旅游金混合支付。但对于不同的线路,旅客可以自由选择不同的支付方式。

森森决定从 1 号城市出发,到 n 号城市去。他打算在出发前准备一些现金,并在途中的某个城市将剩余现金 全部 换成旅游金后继续旅游,直到到达 n 号城市为止。当然,他也可以选择在 1 号城市就兑换旅游金,或全部使用现金完成旅程。

Z 省政府会根据每个城市参与活动的情况调整汇率(即调整在某个城市 1 元现金能换多少旅游金)。现在你需要帮助森森计算一下,在每次调整之后最少需要携带多少现金才能完成他的旅程。

输入格式:
输入在第一行给出三个整数 n,m 与 q(1≤n≤105​,1≤m≤2×105,1≤q≤105),依次表示城市的数量、旅行线路的数量以及汇率调整的次数。

接下来 m 行,每行给出四个整数 u,v,c 与 d(1≤u,v≤n,1≤c,d≤109),表示一条从 u 号城市通向 v 号城市的有向旅行线路。每次通过该线路需要支付 c 元现金或 d 元旅游金。数字间以空格分隔。输入保证从 1 号城市出发,一定可以通过若干条线路到达 n 号城市,但两城市间的旅行线路可能不止一条,对应不同的收费标准;也允许在城市内部游玩(即 u 和 v 相同)。

接下来的一行输入 n 个整数 a1,a2,⋯,an(1≤ai≤109),其中 ai表示一开始在 i 号城市能用 1 元现金兑换 ai个旅游金。数字间以空格分隔。

接下来 q 行描述汇率的调整。第 i 行输入两个整数 xi 与 ai′(1≤xi≤n,1≤a
​i′ ≤109),表示第 i 次汇率调整后,xi号城市能用 1 元现金兑换 ai′个旅游金,而其它城市旅游金汇率不变。请注意:每次汇率调整都是在上一次汇率调整的基础上进行的。

输出格式:
对每一次汇率调整,在对应的一行中输出调整后森森至少需要准备多少现金,才能按他的计划从 1 号城市旅行到 n 号城市。

再次提醒:如果森森决定在途中的某个城市兑换旅游金,那么他必须将剩余现金全部、一次性兑换,剩下的旅途将完全使用旅游金支付。

输入样例:
6 11 3
1 2 3 5
1 3 8 4
2 4 4 6
3 1 8 6
1 3 10 8
2 3 2 8
3 4 5 3
3 5 10 7
3 3 2 3
4 6 10 12
5 6 10 6
3 4 5 2 5 100
1 2
2 1
1 17

输出样例:
8
8
1

样例解释:
对于第一次汇率调整,森森可以沿着 1→2→4→6 的线路旅行,并在 2 号城市兑换旅游金;

对于第二次汇率调整,森森可以沿着 1→2→3→4→6 的线路旅行,并在 3 号城市兑换旅游金;

对于第三次汇率调整,森森可以沿着 1→3→5→6 的线路旅行,并在 1 号城市兑换旅游金。

分析:主体思路是,用Dijskra最短路算法分别算出:
1.使用现金从城市1出发,到达所有城市的最小花费(储存在cashD内)
2.使用旅游金从城市n出发,到达所有城市的最小花费(储存在voucherD内)
这样我们就能通过枚举中转点的方式,得到在第i个城市将现金换成旅游金的情况下所需要的现金总额~就是使用从城市1到达第i个城市所需要的最小现金数 + 从第i个城市到城市n所需要的最小旅游金数所转换成的现金数量,就可以得到在第i个城市兑换所需要的总现金费用,储存在tran[i]内~
huan[i]中储存第i个城市的汇率,cashE[i]和voucherE[i]中储分别储存使用现金和旅游金到达i可下一个城市的花费。最后将所有中转点所要用的花费储存在一个可以有重复值的multiset容器 minCost 中。最后在更新汇率时,将更新前花费从 minCost 中删除,并插入新值,然后输出 minCost 中的最小值就是答案啦~
注意:如果某点使用现金或旅游金不可达,那么该点的tran值则设置为0,在更新时改点也无需操作~

L3-025 那就别担心了 (30 分)-PAT 团体程序设计天梯赛 GPLT

下图转自“英式没品笑话百科”的新浪微博 —— 所以无论有没有遇到难题,其实都不用担心。

博主将这种逻辑推演称为“逻辑自洽”,即从某个命题出发的所有推理路径都会将结论引导到同一个最终命题(开玩笑的,千万别以为这是真正的逻辑自洽的定义……)。现给定一个更为复杂的逻辑推理图,本题就请你检查从一个给定命题到另一个命题的推理是否是“逻辑自洽”的,以及存在多少种不同的推理路径。例如上图,从“你遇到难题了吗?”到“那就别担心了”就是一种“逻辑自洽”的推理,一共有 3 条不同的推理路径。

输入格式:
输入首先在一行中给出两个正整数 N(1<N≤500)和 M,分别为命题个数和推理个数。这里我们假设命题从 1 到 N 编号。

接下来 M 行,每行给出一对命题之间的推理关系,即两个命题的编号 S1 S2,表示可以从 S1 推出 S2。题目保证任意两命题之间只存在最多一种推理关系,且任一命题不能循环自证(即从该命题出发推出该命题自己)。

最后一行给出待检验的两个命题的编号 A B。

输出格式:
在一行中首先输出从 A 到 B 有多少种不同的推理路径,然后输出 Yes 如果推理是“逻辑自洽”的,或 No 如果不是。题目保证输出数据不超过 10​9​​ 。

输入样例 1:
7 8
7 6
7 4
6 5
4 1
5 2
5 3
2 1
3 1
7 1

输出样例 1:
3 Yes

输入样例 2:
7 8
7 6
7 4
6 5
4 1
5 2
5 3
6 1
3 1
7 1

输出样例 2:
3 No

分析:Edge中储存第i个命题可以推出的命题编号,A、B表示起始路径和终点路径,laneNum[i]储存从第i个命题到第B个命题有多少种不同的推理路径,laneNum[B]的初始值为1。consistency表示推理是否逻辑自洽。通过记忆化dfs从A点开始更新laneNum的值,index表示当前所在的命题编号,如果laneNum[index]为0则表示已经向后搜索过,直接返回其数值即可~将laneNum[index]加上它所有可以到达的后续节点Next的laneNum值,如若某点的laneNum值为0,则表示从A出发到这一命题,不能继续向下推理到命题B,则标记consistency为1~

L2-039 清点代码库 (25 分)-PAT 团体程序设计天梯赛 GPLT

上图转自新浪微博:“阿里代码库有几亿行代码,但其中有很多功能重复的代码,比如单单快排就被重写了几百遍。请设计一个程序,能够将代码库中所有功能重复的代码找出。各位大佬有啥想法,我当时就懵了,然后就挂了。。。”

这里我们把问题简化一下:首先假设两个功能模块如果接受同样的输入,总是给出同样的输出,则它们就是功能重复的;其次我们把每个模块的输出都简化为一个整数(在 int 范围内)。于是我们可以设计一系列输入,检查所有功能模块的对应输出,从而查出功能重复的代码。你的任务就是设计并实现这个简化问题的解决方案。

输入格式:
输入在第一行中给出 2 个正整数,依次为 N(≤10​4​​ )和 M(≤102​​),对应功能模块的个数和系列测试输入的个数。

随后 N 行,每行给出一个功能模块的 M 个对应输出,数字间以空格分隔。

输出格式:
首先在第一行输出不同功能的个数 K。随后 K 行,每行给出具有这个功能的模块的个数,以及这个功能的对应输出。数字间以 1 个空格分隔,行首尾不得有多余空格。输出首先按模块个数非递增顺序,如果有并列,则按输出序列的递增序给出。
注:所谓数列 { A​1​​ , …, A​M } 比 { B1, …, B​M } 大,是指存在 1≤i<M,使得 A​1​​ =B​1 ,…,A​i =B​i​​ 成立,且 Ai+1​​ >B​i+1。

输入样例:
7 3
35 28 74
-1 -1 22
28 74 35
-1 -1 22
11 66 0
35 28 74
35 28 74

输出样例:
4
3 35 28 74
2 -1 -1 22
1 11 66 0
1 28 74 35

分析:将每一个功能模块的输出放在vector<int>中(用temp储存),并将其储存在map容器A内。最后将数量与模块输出储存在可以有多个相同key值的multimap容器B内,因为按照增序数出,所以添加一个容器参数greater<int>。这样一来B内就是排好序的答案啦,然后直接输出答案即可~

L2-040 哲哲打游戏 (25 分)-PAT 团体程序设计天梯赛 GPLT

哲哲是一位硬核游戏玩家。最近一款名叫《达诺达诺》的新游戏刚刚上市,哲哲自然要快速攻略游戏,守护硬核游戏玩家的一切!

为简化模型,我们不妨假设游戏有 N 个剧情点,通过游戏里不同的操作或选择可以从某个剧情点去往另外一个剧情点。此外,游戏还设置了一些存档,在某个剧情点可以将玩家的游戏进度保存在一个档位上,读取存档后可以回到剧情点,重新进行操作或者选择,到达不同的剧情点。

为了追踪硬核游戏玩家哲哲的攻略进度,你打算写一个程序来完成这个工作。假设你已经知道了游戏的全部剧情点和流程,以及哲哲的游戏操作,请你输出哲哲的游戏进度。

输入格式:
输入第一行是两个正整数 N 和 M (1≤N,M≤10​5​​),表示总共有 N 个剧情点,哲哲有 M 个游戏操作。

接下来的 N 行,每行对应一个剧情点的发展设定。第 i 行的第一个数字是 K​i,表示剧情点 i 通过一些操作或选择能去往下面 Ki个剧情点;接下来有 Ki个数字,第 k 个数字表示做第 k 个操作或选择可以去往的剧情点编号。

最后有 M 行,每行第一个数字是 0、1 或 2,分别表示:

0 表示哲哲做出了某个操作或选择,后面紧接着一个数字 j,表示哲哲在当前剧情点做出了第 j 个选择。我们保证哲哲的选择永远是合法的。
1 表示哲哲进行了一次存档,后面紧接着是一个数字 j,表示存档放在了第 j 个档位上。
2 表示哲哲进行了一次读取存档的操作,后面紧接着是一个数字 j,表示读取了放在第 j 个位置的存档。
约定:所有操作或选择以及剧情点编号都从 1 号开始。存档的档位不超过 100 个,编号也从 1 开始。游戏默认从 1 号剧情点开始。总的选项数(即 ∑Ki)不超过 106。

输出格式:
对于每个 1(即存档)操作,在一行中输出存档的剧情点编号。

最后一行输出哲哲最后到达的剧情点编号。

输入样例:
10 11
3 2 3 4
1 6
3 4 7 5
1 3
1 9
2 3 5
3 1 8 5
1 9
2 8 10
0
1 1
0 3
0 1
1 2
0 2
0 2
2 2
0 3
0 1
1 1
0 2

输出样例:
1
3
9
10

样例解释:
简单给出样例中经过的剧情点顺序:

1 -> 4 -> 3 -> 7 -> 8 -> 3 -> 5 -> 9 -> 10。

档位 1 开始存的是 1 号剧情点;档位 2 存的是 3 号剧情点;档位 1 后来又存了 9 号剧情点。

分析:node表示存档信息格式,其中pos表示当前所在剧情点编号,Path表示当前的游戏内所走的路径。Edge[i]中储存第i个剧情点可通往的剧情编号,Save中储存存档信息,now表示当前所在剧情点。之后按题意模拟即可~

L2-038 病毒溯源 (25 分)-PAT 团体程序设计天梯赛 GPLT

病毒容易发生变异。某种病毒可以通过突变产生若干变异的毒株,而这些变异的病毒又可能被诱发突变产生第二代变异,如此继续不断变化。

现给定一些病毒之间的变异关系,要求你找出其中最长的一条变异链。

在此假设给出的变异都是由突变引起的,不考虑复杂的基因重组变异问题 —— 即每一种病毒都是由唯一的一种病毒突变而来,并且不存在循环变异的情况。

输入格式:
输入在第一行中给出一个正整数 N(≤10​4​),即病毒种类的总数。于是我们将所有病毒从 0 到 N−1 进行编号。

随后 N 行,每行按以下格式描述一种病毒的变异情况:

k 变异株1 …… 变异株k

其中 k 是该病毒产生的变异毒株的种类数,后面跟着每种变异株的编号。第 i 行对应编号为 i 的病毒(0≤i<N)。题目保证病毒源头有且仅有一个。

输出格式:
首先输出从源头开始最长变异链的长度。
在第二行中输出从源头开始最长的一条变异链,编号间以 1 个空格分隔,行首尾不得有多余空格。如果最长链不唯一,则输出最小序列。

注:我们称序列 {a​1 ,⋯,an} 比序列 {b​1​​ ,⋯,bn} “小”,如果存在 1≤k≤n 满足 ai​​ =b​i​​ 对所有 i<k 成立,且 a​k​​ < b​k​​ 。

输入样例:
10
3 6 4 8
0
0
0
2 5 9
0
1 7
1 2
0
2 3 1

输出样例:
4
0 4 9 1

分析:Edge[i]中保存第i种病毒可能产生的变异病毒编号,in[i]中保存编号i的入度,入度为零的病毒为源头病毒,pa[i]中保存病毒由哪种病毒变异而来(上级病毒)。为了保证序列最小,在输入Edge[i]后可对其进行一次排序~然后通过BFS找到变异次数,过程中由Long和ans标记最多变异次数量以及对应的病毒编号,最后通过pa找回完整变异链~