
算法竞赛讲义
文章平均质量分 94
算法竞赛教学所用的讲义
风中的微尘
这个作者很懒,什么都没留下…
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
39.离散化与哈希
摘要 本文介绍了离散化和字符串哈希两种数据处理技术。离散化通过将大范围数据映射为连续小整数,降低算法复杂度,包含排序、去重和二分查找三个步骤。字符串哈希部分未展开说明。最后提供了离散化相关的3道练习题(火烧赤壁、Overplanting S、程序自动分析)和字符串哈希相关的2道练习题(字符串哈希模板、积木小赛)。这两种技术常用于算法竞赛中优化数据处理效率。原创 2025-08-16 18:49:56 · 383 阅读 · 0 评论 -
37.字典树
摘要:字典树(Trie)是一种高效的字符串处理数据结构,通过共享前缀构建多叉树结构,实现快速字符串检索、排序和统计。其核心特点是根节点不存储字符,其他节点存储单个字符,利用空间换取时间效率(O(n)查询)。文章介绍了字典树的基本操作实现,包括初始化、插入、删除和查询,并提供了相关练习题(黄题到绿题难度)。字典树适用于字符串前缀匹配、词频统计等场景,但可能占用较大空间(如26个子节点的小写字母情况)。原创 2025-08-05 18:59:49 · 848 阅读 · 0 评论 -
36.Manacher 算法
本文介绍了Manacher算法,这是一种用于高效求解字符串中最长回文子串的线性时间算法。文章首先说明了回文字符串的定义,并指出处理奇偶长度回文串的方法:通过插入特殊字符将字符串统一转换为奇数长度形式。然后详细分析了算法的核心思路,包括定义d[i]数组表示以i为中心的回文半径,并推导出最终答案为max(d[i]-1)。重点阐述了如何利用对称性和已有信息高效计算d[i]数组,给出了关键代码实现。最后提供了三道练习题,涵盖从绿题到蓝题的不同难度级别,帮助读者巩固算法应用。原创 2025-07-29 18:22:04 · 604 阅读 · 0 评论 -
35.KMP 算法
本文介绍了两种高效的字符串匹配算法:KMP算法和扩展KMP算法。KMP算法通过预处理模式串生成Next数组,避免重复匹配,时间复杂度为O(n+m)。扩展KMP(Z算法)用于求解文本串所有后缀与模式串的最长公共前缀,同样利用预处理思想,复杂度为O(|S|+|T|)。文章详细讲解了两个算法的匹配过程和Next数组的构建方法,并提供了核心代码实现。最后还给出了相关练习题,包括模板题和进阶题目,帮助读者巩固KMP和扩展KMP算法的应用。这些算法在字符串处理中具有重要价值,能显著提升匹配效率。原创 2025-07-15 18:38:30 · 945 阅读 · 0 评论 -
34.树形 DP
本文介绍了树形动态规划的基本概念和应用实例。主要内容包括:1)树形DP的基本思想,即在树结构上通过递归和回溯维护状态最优解;2)两个典型例题分析:"没有上司的舞会"(选择不相邻节点的最大权重)和"二叉苹果树"(保留指定边数的最大苹果数),分别给出了状态转移方程和核心代码;3)作业题单,包含黄、绿、蓝三个难度级别的树形DP练习题。文章通过具体示例展示了树形DP的建模方法和实现技巧,适合算法学习者理解和练习树形动态规划问题。原创 2025-06-14 15:44:46 · 810 阅读 · 0 评论 -
33.状态压缩动态规划
若元素数量比较小(不超过20)时,想要存储每个元素取或不取的状态时, 可以借助位运算将状态压缩。需要借助状态压缩过程的动态规划就是状态压缩 DP(很多地方会简称为取若干元素,也就是对应的位置记为1,其余位置记为0。例如,一共有5个元素abcde,我们分别用二进制的110100100010000表示这五个元素,则集合ace可以用10101221来表示,而集合bcd可以用01110214表示。对于元素个数为n的情况,其空间复杂度为O2n。原创 2025-04-26 13:43:52 · 853 阅读 · 0 评论 -
32.图的连通性问题
若一张无向图的节点两两互相可达,则称这张图是。G是一个无向图。若H是G的一个连通子图,且不存在F满足H⊊F⊆G且F为连通图,则H是G的一个。若一张有向图的节点两两互相可达,则称这张图是。若一张有向图的边替换为无向边后可以得到一张连通图,则称原来这张有向图是。与连通分量类似,也有。原创 2025-03-01 13:53:17 · 851 阅读 · 0 评论 -
30.线段树与树状数组基础
树状数组是一种支持单点修改和区间查询的,代码量小的数据结构。普通树状数组维护的信息及运算要满足结合律且可差分,如加法(和)、乘法(积)、异或等。x∘y∘zx∘y∘zx∘y∘zx∘y∘z,其中∘\circ∘是一个二元运算符。可差分:具有逆运算的运算,即已知x∘yx\circ yx∘y和xxx可以求出yyy。事实上,树状数组能解决的问题是线段树能解决的问题的子集:树状数组能做的,线段树一定能做;线段树能做的,树状数组不一定可以。原创 2023-12-02 00:02:37 · 1422 阅读 · 0 评论 -
29.组合数学
排列组合是组合数学的基础内容,学好基础的排列组合部分才能在后续更高级的算法种熟练应用。:指从给定个数的元素中取出指定个数的元素进行排序。:指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。排列组合的核心问题是研究给定要求的排列或组合可能出现的情况总数。排列组合一般与古典概型关系密切。在高中数学中,排列组合多是利用列表、枚举等方法解题,并不涉及很复杂的组合数学知识。原创 2023-10-21 10:59:50 · 646 阅读 · 0 评论 -
28.直径与重心
对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心。子树:指无根树的子树,即包括向上的那棵子树,并且不包括整棵树自身。原创 2023-09-23 15:16:34 · 336 阅读 · 1 评论 -
27.最近公共祖先问题
有时候因为题目特性,倍增求 LCA 可能会比较慢。而由于一些强制在线的原因, tarjan 离线的方法求 LCA 的方法也不再适用,这时候就需要下面讲到的树链剖分算法进行求解了。主要思想是利用轻重链的划分来快速在树上进行跳转。原创 2023-08-30 18:02:08 · 275 阅读 · 0 评论 -
26.最小生成树
Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明。该算法的基本思想是从小到大加入边,是个贪心算法,一般需要借助并查集这一数据结构来实现。Prim 算法是另一种常见并且好写的最小生成树算法。该算法的基本思想是从一个结点开始,不断加点,这和以加边的方式进行的 Kruskal 算法有所不同。在算法的流程上与 Dijkstra有更多的相似之处。原创 2023-08-26 12:17:45 · 381 阅读 · 0 评论 -
25.并查集
如果树边不只是表示两者同属一个集合的关系,还涉及到边权的问题,那么这样的并查集就变成了带权并查集,查询与合并时需要进行的操作都有很多的不同。原创 2023-08-23 13:42:00 · 223 阅读 · 0 评论 -
24.最短路问题
解决单源最短路径问题常用 Dijkstra 算法,用于计算一个顶点到其他所有顶点的最短路径。Dijkstra 算法的主要特点是以起点为中心,逐步向外扩展,每次都会取一个最近点继续扩展,直到取完所有点为止。注意:Dijkstra 算法要求图中不能出现负权边。原创 2023-07-28 14:52:33 · 419 阅读 · 0 评论 -
23.简单背包问题
有N件物品和一个容量是V的背包。。第i件物品的体积是wi,价值是vi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。原创 2023-07-21 22:54:43 · 582 阅读 · 0 评论 -
22.动态规划入门
本次课我们将介绍介绍动态规划(Dynamic Programming, DP)及其解决的问题、根据其设计的算法及优化。动态规划是算法竞赛当中一种非常重要手段,它是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。由于动态规划并不是某种具体的算法,而是一种解决特定问题的方法,因此它会出现在各式各样的数据结构中,与之相关的题目种类也更为繁杂。原创 2023-07-07 23:38:55 · 738 阅读 · 0 评论 -
21.拓扑排序与欧拉图
拓扑排序的英文名是 Topological sorting。拓扑排序要解决的问题是给一个图的所有结点排序,使得排在前面的结点不能于排在后面的结点。在一个DAG(有向无环图)中,我们将图中的顶点以线性方式进行排序,使得对于任何的顶点u到v的有向边uv, 都可以有u在v的前面。若给定一个DAG,如果从i到j有边,则认为j依赖于i。如果i到j有路径,则称i间接依赖于j。若是有环图,则不存在拓扑排序,原因显而易见。原创 2023-06-24 01:27:27 · 460 阅读 · 0 评论 -
20.图和树基础
描述的是一些个体之间的关系。这些个体之间既不是前驱后继的顺序关系,也不是祖先后代的层次关系,而是错综复杂的网状关系。我们一般用GVE来表示,V表示结点,E表示边。根据边是否有,分为带权图和不带权图,也可将不带权图视为权重都为1的图。根据边是否有,分为有向图和无向图。根据稠密程度(边的条数∣E∣与∣V∣2)的关系,分为稠密图和稀疏图。原创 2023-01-28 14:53:45 · 991 阅读 · 0 评论 -
19.广度优先搜索
广度优先搜索 BFS(Breadth First Search)按照广度优先的方式进行搜索,可以理解为“尝试所有下一步可能”地穷举所有可行的方案,并不断尝试,直到找到一种情况满足问题的要求。BFS 从起点开始,优先搜索离起点最近的点,然后由这个最近的点扩展其他稍近的点,这样一层一层的扩展,就像水波扩散一样。故而可以把 BFS 当成并行计算的一种模拟。广度优先搜索可以用队列实现,而且几乎都是用队列实现,所以要学会广度优先搜索,首先必须要学会队列。原创 2023-04-15 09:53:12 · 1216 阅读 · 1 评论 -
18.深度优先搜索
深度优先搜索DFS(Depth First Search)按照深度优先的方式进行搜索,可以理解为“一条路走到黑”地穷举所有可行的方案,并不断尝试,直到找到一种情况满足问题问题的要求。那么这个方案就是一个问题的解。原创 2023-02-01 00:06:01 · 779 阅读 · 0 评论 -
17.高精度计算
高精度计算(Arbitrary-Precision Arithmetic),也被称作大整数(bignum)计算,运用了一些算法结构来支持更大整数间的运算(数字大小超过语言内建整型)。原创 2023-03-31 23:34:09 · 1129 阅读 · 0 评论 -
16.二分法
二分法是一种基础但非常精妙的算法,经常能为我们打开解题的思路,也常常作为题目的其中一个重要环节出现。二分的基本用法就是在一个单调序列或单调函数中进行参照点(中心点)的移动。通过不断尝试并每次缩小一半的区间或者取值范围,来最终逼近我们的答案。根据参照点(中心点)的意义,我们分为二分查找和二分答案两大内容。二分答案常常与有关系。二分法还可以衍生出三分法,不过其适用范围很小。如果需要求出单峰函数的极值点,通常使用二分法衍生出的三分法求单峰函数的极值点。原创 2023-05-27 12:16:43 · 1027 阅读 · 0 评论 -
15.贪心算法
贪心算法是指在对问题求解时,总是做出在看来是最好的选择,而不考虑后续可能造成的影响。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。在贪心算法的应用当中,往往都会和排序联系起来,所以想要熟练使用贪心算法则必须首先能够熟练地应用排序。原创 2023-05-12 22:30:06 · 833 阅读 · 0 评论 -
14.基础数论2
任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积Np1a1p2a2p3a3...pnan,这里p1p2...pn均为质数,其中指数ai是正整数。这样的分解称为N的标准分解式。原创 2023-01-25 01:27:34 · 682 阅读 · 0 评论 -
13.基础数论1
给定一个正整数mmm,两个整数aba,bab叫做模mmm同余,如果a−ba-ba−b被mmm整除,或m∣a−bm\mid a-bm∣a−b,就记作a≡bmodma≡bmodm。否则,叫做模mmm不同余,记作a≢bmodma≡bmodm。原创 2023-01-18 12:36:03 · 715 阅读 · 0 评论 -
12.质数与筛法
欧拉筛是一个能够做到OnO(n)On的时间复杂度,也就是线性的质数筛法,是目前性能最优秀的质数筛法。在很多算法和数据结构题目中都有大量的应用,是一个十分基础的工具。对于一个频繁使用的工具,我们从原理上掌握它是非常有必要的。原创 2023-01-04 13:10:54 · 410 阅读 · 0 评论 -
11.stack和queue
使用 STL 的栈需要首先在代码的开头引入这个头文件。stack s;TTT表示我们所定义的数组存储数据的类型。它可以是这样的基本数据类型,可以是typedef之后的代词,也可以是自定义的结构体类型,甚至可以是其他的 STL 类型。一开始定义好的栈的长度为000,代表一个空的栈。队列是 OI 中常用的一种满足一定约束的线性数据结构。队列只允许在其中一端插入,在另一端删除元素,一端被称为队首,另一端被称为队尾。故而队列满足一种“先进先出的原则”。原创 2023-01-14 01:12:25 · 999 阅读 · 0 评论 -
10.vector、set和map
有些时候想开一个数组,但是却不知道应该开多大长度的数组合适,因为我们需要用到的数组可能会根据情况变动。这时候我们就需要用到动态数组。所谓动态数组,也就是不定长数组,数组的长度是可以根据我们的需要动态改变的。动态数组的实现也不难,但是在 C++ 里面有已经写好的标准模板库 STL,实现了集合、映射表、栈、队列等数据结构和排序、查找等算法。我们可以很方便地调用标准库来减少我们的代码量。C++ 中动态数组写作vector,C 语言中没有标准模板库,这也是为什么参加比赛推荐用 C++ 而不用 C。原创 2023-01-04 11:14:50 · 746 阅读 · 0 评论 -
9.指针与链表基础
链表是一种用于存储数据的数据结构,通过如链条一般的指针来连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳。链表和数组都可用于存储数据。与链表不同,数组将所有元素按次序依次存储。不同的存储结构令它们有了不同的优势:链表删除和插入数据的时间复杂度是O1O(1)O1。寻找、读取数据的时间复杂度是OnO(n)On。链表删除和插入数据的时间复杂度是OnO(n)On。寻找、读取数据的时间复杂度是O1O(1)O1。//指针ll value;原创 2024-10-18 22:04:54 · 1249 阅读 · 0 评论 -
8.结构体
可以将任意多不同或者相同的数据类型整合到一个数据类型当中,包括整型、浮点型、字符、数组、指针和结构体自身等等。它可以看作是一种自定义类型,在后续使用时当作如int这样的基本数据类型来使用。一个结构体变量包含定义中所有的变量,每一个变量称为结构体的一个。下面是一个普通的结构体的例子,在这个叫做 node 的结构体中包含了一些常见的数据类型,它有6个成员,其中有两个成员是数组。所以整个结构体总共包含了204个变量。原创 2022-11-26 16:11:34 · 1243 阅读 · 0 评论 -
7.排序与sort
本节将简要介绍各种排序算法,并给出模板以及其性能分析。是一种将一组特定的数据按某种顺序进行排列的算法。排序算法多种多样,性质也大多不同。原创 2023-01-04 13:13:26 · 534 阅读 · 0 评论 -
6.函数与递归
此前我们使用了很多库函数,现在我们可以定义自己的函数来帮助我们完成一些特定的任务。函数返回值类型有很多类:变量类型同样如此,可以为任意类型。函数的定义必须在使用之前,否则编译器将会报错。通常情况下,函数可以以如下方式定义并调用,函数之间也可以来回调用。但是在某些比较复杂的递归调用中,我们没有办法满足先定义后使用的条件,比如:面对这种情况,我们可以提前声明函数,就可以不考虑函数执行的先后顺序来写函数了。3.形参和实参主函数和函数中都有参数和,但因其作用域没有相交,所以可以使用。函数中的局部变量名称也原创 2022-12-03 10:52:11 · 714 阅读 · 0 评论 -
5.字符串与字符数组
一个S是将n个字符顺次排列形成的序列,n称为S的长度,表示为∣S∣。如果字符串下标从1开始计算,S的第i个字符表示为Si如果字符串下标从0开始计算,S的第i个字符表示为Si−1。原创 2022-11-26 16:09:29 · 942 阅读 · 0 评论 -
4.数组与基本数学函数
数组的声明形如a[len],其中,a是数组的名字,len是数组中元素的个数。在编译时,len应该是已知的,也就是说,len应该是一个整型的表达式。通常使用常量或者宏定义,也可以是变量,但一般不建议使用。// 错误 arr2 = arr1;// 错误应该尽量将较大的数组定义为全局变量。因为局部变量会被创建在栈区中,过大的数组会爆栈,进而导致 RE(run time error)。如果将数组声明在全局作用域中,就会在堆中创建数组。原创 2023-08-04 23:07:36 · 386 阅读 · 0 评论 -
3.分支与循环
一个 CPP 程序默认是按照代码书写顺序,从上到下依次执行下来的。但是,有时我们需要选择性的执行某些语句,来实现更加复杂的逻辑,这时候就需要分支结构语句的功能来实现。选择合适的分支语句可以显著提高程序的效率。原创 2023-08-01 14:58:16 · 196 阅读 · 1 评论 -
2.基本运算
类型转换规则如下所示:位运算符用于对二进制数进行按位运算,这是计算机最底层的运算法则。学习位运算对后续诸多算法和数据结构都有很大的帮助。有时我们需要让变量进行增加 111 或者减少 111,这时自增运算符 和自减运算符 就派上用场了。自增/自减运算符可放在变量前或变量后面,在变量前称为前缀,在变量后称为后缀,单独使用时前缀后缀无需特别区别,如果需要用到表达式的值则需注意,具体可看下面的例子。五、比较运算符运算符功能大于大于等于小于小于等于等于原创 2023-07-28 23:28:49 · 336 阅读 · 0 评论 -
1.CPP基本介绍
是一个用于输入输出流(input and output stream)的库,里面包含一系列可以用于输入、输出的方法。在 C 或 C++ 中,程序的返回值不为 0 会导致运行时错误(RE)。变量可以被看做一个盒子,每个盒子中可以存储一定的值。C++ 为了和 C 保持兼容,可以直接使用部分 C 语言中的头文件,例如。类型一般为 8 位,底层存储的方式依然是整数,一般通过。给左侧的一个变量,让变量这个盒子存储这一个值。如果修改了常量的值,在编译环节就会报错。,表示标准输入输出流。中的代码,这是整个程序的入口。原创 2023-07-26 12:44:38 · 2495 阅读 · 0 评论 -
数学基础1
..−4−3−2−101234...Z31581.37...有理数:整数和分数统称为有理数。有理数集用Q表示。aa∣a∣。原创 2023-11-11 00:15:14 · 675 阅读 · 0 评论