算法期末复习指北

叠甲声明

  • 这是一份并不算详细的算法复习指北,不包含算法详细讲解(想看详细讲解的去翻PPT或者找网上博客或者找oi-wiki),也不包含代码模板,仅仅是对大纲和考点的一些浅薄的个人理解,希望能给一些不知道怎么复习的学弟学妹给到一点帮助。

  • 本文重点在于基础知识点考点的分析讲解,不太会涉及到考试中难度较大的题目,如果你平时能够独立完成代码并稳定在前20名,那么这份指南可能对你帮助不大,你现在也许可以退出,去复习其它科目。

  • 本文所有关于考点重点的推断均为个人臆断,仅代表个人观点,与出题组无关,与最终期末试题无关。

    出题组看到这篇指南后不要针对这篇指南的重点反向出题,给鄙人留点面子球球了TAT

  • 为了做这份资料把这届所有C系列和E系列的题目都翻了一遍,制作不易,有小错误(比如题号有可能不小心敲错)或者不满意的地方还请多多包涵。

  • 如果有觉得不对或者有疑问的地方欢迎私信。

复习思路

模板

算法复习的一件比较重要的事情是把考试要用的模板准备好。我们课程中后期学过的图论、计算几何、FFT、字符串等知识点都是需要板子的,一个好用的板子可以极大提高你的做题效率。所以你复习算法的第一件事情是把板子准备好

我们的C系列和E系列题目一般会将各种算法的模板题都至少出一次,按理来说,我们的板子应该在当时就准备好了;如果现在没有准备好,那么你期末考试之前一定是需要抽出时间来准备板子的。

需要注意的是,准备板子不能是随便在网上复制一份代码、发现能过OJ的模板题、就打印出来。我们不要求理解整个算法流程,但是至少是需要知道这个板子大致在干什么的,图论模板怎么加边?加的是有向边还是无向边?你的凸包模板会不会保留三点共线中中间的点?如果你的FFT是用数组而非std::vector实现的,你知道数组应该开多大吗?

举个例子,C6中B题和C题都是FFT题,C题只不过是将B题中的十进制换成了八进制,也就是把代码中的某个10换成8,最终B题的通过人数是99,C题的通过人数是57,所以说当时可能很多人使用的代码是B题原题的代码而非自己用FFT模板改过来的代码,而对于现成的B题代码并不会如何修改成C题的要求,这样的板子打印出来,在考场上也没什么用。

另外,都已经是抄板子、可以直接用别人的代码了,就抄点好的,抄码量少的,抄性能好速度快的,抄封装得好的。关于码量少,个人不太建议整那些一堆classpublicprivate(当然如果你已经整了并且自己会用,那完全OK)。算法做题追求代码精简高效,写得太长到时候也不方便抄,我们的算法虽然不算简单,但也都是最多一两百行代码能解决的,没有复杂到需要那一堆面向对象的东西来封装。关于性能好,举个例子就是FFT能抄迭代版就不抄递归版,别到时候RE了不知道为啥。

有的同学也许会期待我提供模板,但是很抱歉,没有。我有一套绝妙的板子,但是这个文档地方太小了,写不下。

人很难保证自己的板子是100%正确的,有的板子可以过模板题,但是可能实际上代码考虑得并不完善,或者特定题目中会出现一些模板没有考虑到的情况(例如图论中的重边问题)。即使我对于自己的板子很自信,但是一百多号人的3学分的考试成绩的责任是重大的,也是我难以承担的。鉴于此,我没有办法在这里放出自己的板子。

刷题

与其它科目不一样的是,算法可能并没有太多往年题可供参考,大概也没有提交渠道。虽然别的地方(比如洛谷等在线评测网站)也可以提供一些算法练习,但是这里建议复习的话还是以C系列和E系列考过的题目为主,毕竟这些题目都是完全基于课程要求的知识点的,而且你可以通过本院过题人数大概了解题目难度、在想不明白的时候明确是否有必要深究下去。

时间充裕的话你可以把之前的题目做一遍,没有时间的话就看一看、构思一下算法流程,觉得自己能写就可以开下一题了。重点可以看一下自己WA了很多遍的题目,复盘一下当时为什么WA,也许可以收获一些有用的经验。

复习重点

你软大二上的任务量非常大,计组OOP离散概统都是需要花时间复习的,留给算法的时间可能并不多,所以复习的时候应该有所取舍。

首先,个人观点是,复习算法流程是没有那么必要的,例如最短路Dijkstra,你知道这个算法是用来求图的单源最短路的,至于它怎么求的、过程是怎样的、为什么是对的,这种内容虽然在课上讲的时候是重点,选择题也可能会考,但是在期末复习时间紧张的情况下可以不再深究,你只需要会使用模板就OK了。

还是拿Dijkstra举例子,明白怎么使用单源最短路板子之后,你需要知道的是如何在这个基础上如何去解决类似于E4D这种题;对于拐了个弯的非模板题,这也许需要一些巧思,并没有固定的解题公式。

当然,考试完全可能给你一个需要魔改原算法、需要理解算法的题目,但是这种题的话大概全体通过率不会太高?一个课内的例子是E4G,给原本最短路的算法加了一个“走了几条边”,算是需要一些对算法的基本理解了。

另外,根据个人实际情况,部分知识点也许可以稍微开摆,关于这点我下一个部分再进行详述。

基础考点复习

选择题

关于考点,这部分也许会考一些主定理、某些算法时间复杂度、某些算法的原理概念,emmm大概这些内容。

关于拿分,个人觉得选择题这部分是不太具有性价比的分数,至少在我个人这里优先级不是太高。

一方面,如果需要全面的复习,你需要过完整个PPT/教材,过完之后可能还是会因为一些奇怪的选项不会选;另一方面,所有选择题的分数加起来也不是很多。

我本人的选择是,开场看选择题,会选就选,不会的话,如果自己大致有印象在教材或PPT的哪一部分,就稍微翻翻,如果连在哪都不知道或者觉得可能需要找很久,那就拉倒;等到最后如果后面的题目调试不出来了,再回来翻翻选择题,对照着翻翻书、看能不能排除一两个选项来提高正确率。当然这只是个人的策略,每个人的实际情况不一样,也许有人听课认真、对于知识点的掌握比我好、或者有充裕的时间复习,都可以把选择题的优先级提高一点。

\(\color{white}{暴论:说白了,反正复习了也不一定拿得到分,要真没时间复习,他要就给他}\)

前置基础

一些事情是你在完成了大一的程设和数据结构课之后就应该掌握的,例如基本的模拟、计算和基础数据结构。

例如E1A的暴力枚举,C1B模拟递推,E1B的模拟递归,C1F的数学题,E1C的链表,E6C重新标号,C2E的高精度乘法等等,这些其实都没有涉及什么太难的算法。

排序与堆

首先还是尽量学一下C++里面的std::sortstd::priority_queue,这玩意儿手写起来比较费事儿。

因为考试的时候不排除对个别题目限制C++的可能,在板子的准备方面,如果你平时习惯使用std::sortstd::priority_queue,在期末考试之前你最好准备一下qsort的写法和手写堆的板子。

堆在这一部分这一届的E系列和C系列考得并不多,只有C2D的裸题和E2F,去年的E系列还出过“双堆实现任意删除”,有兴趣的同学可以了解一下,个人感觉是有点意思的思路。

排序的话,除了最常用的快排,对桶排序也需要留个心眼,例如C2C。

排序和堆跟后续的贪心部分联动会比较多。

动态规划DP

动态规划没有模板,对于多数同学来说可能是捉摸不透的,不知道怎么设置状态、列出转移方程,或者说列出来了却不能保证正确性。

一些DP的确是具有思维难度的,而能够逐渐熟练DP的方法也只有刷更多的题目,体会思路和技巧,没有捷径可走。而对于我们期末考试的话,能做的可能只有尽量去熟悉课上和练习中接触过的经典DP题吧。

DP可以是解决计数问题“一共有多少种方案”,也可以是解决最优化问题“我最少花费/最多得到……”。

一般来说我们需要先设定状态,形如“dp[i]表示前i个xx的答案”、“dp[i][j]表示第一个数列只考虑1i、第二个数列只考虑1j的答案”、“dp[i][j]表示区间i到j的答案”、“dp[i][0/1]表示前i个、最后一个的状态为0/1的答案”。

然后我们需要列出状态之间的关系,即状态转移方程,以便我们进行推导。例如dp[i]=dp[i-1]*x+dp[i-2]*y+1;dp[i][j]=max(dp[i][j-1], dp[i-1][j])+a[i][j]等。最后根据初始状态和状态转移方程求得题目要求的答案。

如果说你感觉现在已经不太能想起来当时怎么写DP题的了,建议把前面做过的DP题再过一遍;如果觉得“这个DP就是一个xxxx循环,直接推就完事了”,那么你可以不用进行代码的实现;如果觉得自己实现起来会碰到一定的困难,就选两三题自己再写一写吧。

下面给大家之前做过的题目做一个总结:

  • dp[i]表示i个元素的答案:C3A,C3B,C3D
  • dp[i]表示只考虑前 i 个元素的答案:E2B(难点在于转移方程),E2G(难点在于对于前面每个元素判断与否可以转移过去),C3F(考虑如何限制选择元素不相邻)
  • dp[i][j]表示区间\([i,j]\)的答案:E2I,C3E
  • dp[i][j]表示第一个序列进程为 i ,第二个序列进程为 j 的答案:C4F
  • 经典流水线调度:C3C,C3G,C3J(需要注意方案的输出)(C3G和C3J当时因为题目靠后、通过的人数不多,但是实际上并不难,甚至可以说是很需要掌握的)
  • 经典背包DP:C4D(裸题),E2H(两维分别考虑)

贪心

贪心算法可能并没有一个太准确的界定,在一堆数里面找到最大/小值,可能就算是贪心;有一个相对复杂的策略,然后经过一堆证明,证明这个策略是最右的,这也可能是贪心。

一些贪心策略比较明显、只需要取最值 or 给所有数排序的题:E3A,E3B,C4A(按性价比排序)

有的贪心并不是取最值、而是根据实际情况进行处理,例如E2A贪心地排更多的数字9,E3G贪心地填左括号。

一类比较经典的贪心策略是从左往右贪心,例如C4C,按照右端点排序,从左往右,每次贪心地选择右端点靠左的线段;类似的还有经典的线段覆盖问题;我们练习中的E3E和E6I也可以从左向右贪心;总之从左往右考虑贪心的时候,每次进行的决策都是尽量做对右边剩余情况更好的选择

做题的话,对于贪心策略比较直观的题目直接上手就是了,难的题目看造化了我也没办法,另外WA了之后别光找程序的问题,要反思一下自己的贪心策略有没有毛病。

图论

首先你需要会基本的存图,是用std::vector存还是链式前向星存这个看你自己。即使图论题很多都是搬板子,但是至少图的存储、图的遍历以及进行DFS和BFS还是需要自己会的。

然后就是图论的一堆板子,包括多源最短路Floyed(模板题C5A),单源最短路Dijkstra(+堆优化)(模板题C4E),求负环(SPFA/Bellman-Ford)(模板题C4G),拓扑排序(模板题E4A),网络最大流(模板题E4C),最小费用最大流(模板题E5D),匈牙利算法(模板题E4E),最小生成树(模板题E4B),欧拉回路(模板题C4J)。

(好像有人在准备KM算法?我不是很清楚KM是否在考纲中,但是如果出了KM的题,我们是可以用最小费用最大流来解决的)

不是纯模板的题目:最短路:E4D、E4G;拓扑排序结合DP:C4I。

个人认为这些算法中相对更加重要的是最短路,但是考前的话还是都准备一下吧。

计算几何

同样是非常依仗板子的部分。

除了基本的点和线的存储,还有判断线段/直线相交、平行等功能性的代码,然后是凸包和旋转卡壳。

全部选择用点积和叉积进行各种判断,憋老惦记着你那b斜率了,真不行。

平常练习中做过的基础题包括:C5B,C5C,C5D,C5E,C5F。(感觉旋转卡壳的模板题考场上都没几个人过,我不好说……)

在计算几何部分你尤其需要一个非常靠谱的板子,因为很多板子由于实现不够优雅,精度不够,可能导致你用了人家的的板子最后思路正确但是WA了半天不知道为什么(顺便提一嘴,如果你的计算几何WA了,可以考虑调整一下代码中比较浮点数的EPS)。

这里个人会比较推荐刘汝佳蓝书上的计算几何板子成功甩锅

FFT

首先你需要知道FFT是由DFT和IDFT组成的,我们的E5A考的是DFT、E6A靠的是IDFT,这就涉及到一堆单位根做多项式求值和多项式插值那一套理论。但是个人感觉期末如果要考的话,可能从应用、也就是卷积的角度入手的概率会大一点。

这里的卷积是说,对于数列\(\{a_i\}, \{b_i\}\),它们俩卷积得到的数列 \(\{c_i\}\),满足 \(c_i = \sum_{j=0}^i a_jb_{i-j}\)

我们的E5E、E6B、E6C考的也就是这玩意儿(还有刚刚结束的C7J)。

这里提一个没考过的、有点意思的技巧:我们正常做卷积,是将 \(i+j\) 为定值的所有 \(a_ib_j\) 加到 \(c_{i+j}\) 中;如果将其中一个序列倒过来,其实可以实现将 \(i-j\) 为定值的所有 \(a_ib_j\) 加到 \(c_{n+i-j}\) 中。

字符串

字符串部分涉及的模板包括哈希、KMP(其实还包括数据结构中学过的Trie)。

首先你需要会对字符串进行简单的处理,比如判断回文、字符计数这种类似的事情。

然后个人觉得字符串方面最实用的部分是哈希,可以暴力无脑且快速地对字符串进行比较。对于KMP字符串的题目,我们同样可以用哈希来进行判断匹配。只不过用哈希的时候有可能出现哈希冲突,这个时候你可能需要用更大的模数或者使用双哈希(即使用两套模数)。可以使用哈希的题目包括C6E、C6F、C6G、C6I、E6D。其中C6F、C6G、C6I、E6D也都能用KMP做。

KMP虽然在匹配方面可以被哈希代替,但是其next数组的功能是无法被替代的,如果要做出这部分题目,你需要对字符串前后缀的性质做一个基本的理解,例如E6F。

另外一个处理字符串的好用的工具是std::map,可以用来解决E6E和E6G。

其它技巧

一些其它的技巧,比如二分(E2J,E3D,E4H,C5J,不过这些题目似乎都有点难度),比如随机(C2H),考试的时候不要忘了用。

正式考试

一些想法

算法考试不同于其它考试,命题的不确定性很大,做题的不确定性也很大。

命题方面,正常命题难度下,十道编程题,多数人有机会做的可能只有五道,其中有明确算法考点的可能就三四道,而这三四道题覆盖的知识点是绝对有限的;

做题方面,你自己指不定就有哪题死都调不出来,或者说思维上怎么也拐不过去弯,而在分数上,AC两题和AC三题所得到的分数最终差距是比较大的。

所以说,大家并不要因为自己很不擅长某一个知识点很急,也不要因为突然想起某个地方忘记了而熬夜复习,因为对你来说最重要的一二题可能压根不涉及什么复杂算法。为了让自己在考场上拐过思维的弯、不写低级bug,良好的心态和应试状态才是最关键的。

注意事项

  • 看清楚题面(包括数据范围和时间限制)。

    看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面看清楚题面

  • 跟榜做题,看哪题过的人多就优先开哪题

  • 跟榜做题的同时至少把所有题目的题面都看一遍,AC人数少的题目不一定不适合你,看题并不需要多少时间

  • 不管遇到什么情况都保持心态平和,冷静debug

最后

Finally,祝大家考试顺利!

posted @ 2023-12-13 21:02  Hany01  阅读(227)  评论(0编辑  收藏  举报