【算法笔记】0.开篇

此文是算法笔记系列的开篇,介绍算法竞赛的赛制以及做题技巧,并整理算法相关资料,方便日后查阅。

赛制分类

赛制 是否罚时 分数计算方法 结果反馈
ACM 全部测试点通过才得分
IO 按测试点给分
IOI 按测试点给分

复杂度分析

时间复杂度

算法的时间复杂度「Time Complexity」是一个函数,它定性描述算法的运行时间。它是渐近的,即考察输入值大小趋近无穷时的情况,不是用来代表算法的实际执行时间,而是用于表示代码执行时间的增长变化趋势。

时间复杂度常用大 O 符号 $O(n)$ 表述,大 O 符号,又称渐进符号,是用于描述函数渐近上界的数学符号。

$O(n)$ 数学定义:假如存在正实数 M 和实数 x0,使得对于所有的 x ≥ x0,均有:$|f(x)| ≤ M |g(x)|$ 成立,我们就可以认为,$f(x)=O(g(x))$

$O(log_m^n)$ 通过对数的换底公式,可以得到具有不同底数、相等的时间复杂度,因此通常省略底数,将对数阶直接记为 $O(logn)$

当 $n>=4$ 时恒有 $n! >= 2^n$,所以阶乘阶比指数阶增长得更快!

渐进表示符号如下表「最差(渐进上界)$O$;最佳(渐进下界)$\Omega$;平均 $\Theta$,但常被 $O$ 替代」

image.png

实际中很少使用最佳时间复杂度,因为通常只有在很小概率下才能达到,可能会带来一定的误导性。 而最差时间复杂度更为实用,因为它给出了一个效率安全值,让我们可以放心地使用算法。

平均时间复杂度可以体现算法在随机输入数据下的运行效率,但对于较为复杂的算法,计算平均时间复杂度往往比较困难,因为很难分析出在数据分布下的整体数学期望。在这种情况下,我们通常使用最差时间复杂度作为算法效率的评判标准。

空间复杂度

空间复杂度「Space Complexity」是对一个算法在运行过程中临时占用内存空间大小的一个量度,反映的是随着数据量变大时的增长趋势。

在分析一段程序的空间复杂度时,我们通常统计暂存数据、栈帧空间和输出数据三部分。

image.png

与时间复杂度不同的是,我们通常只关注最差空间复杂度。这是因为内存空间是一项硬性要求,我们必须确保在所有输入数据下都有足够的内存空间预留。

数据范围分析

一般ACM或者笔试题的时间限制是1秒或2秒,在这种情况下,C++代码中的操作次数控制在 $10^7 \sim 10^8$ 为最佳。

根据题目给出的数据范围可反推算法复杂度以及算法内容,下面给出不同数据范围下的代码时间复杂度和算法该如何选择:

数据范围 代码的时间复杂度 算法选择
$n \le 30$ 指数级别 DFS+剪枝,状态压缩DP
$n \le 100$ $O(n^3)$ Floyd,DP,高斯消元
$n \le 1000$ $O(n^2)$,$O(n^2logn)$ DP,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
$n \le 10^4$ $O(n * \sqrt n)$ 块状链表、分块、莫队
$n \le 10^5$ $O(nlogn)$ 各种sort、线段树、树状数组、set/map、heap、
拓扑排序、Dijkstra+heap、Prim+heap、Kruskal、SPFA、
求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树
$n \le 10^6$ $O(n)$
常数较小的 $O(nlogn)$
单调队列、hash、双指针扫描、BFS、并查集,KMP、AC自动机,
常数较小的 $O(nlogn)$ 做法:sort、树状数组、heap、Dijkstra、SPFA
$n \le 10^{7/8}$ $O(n)$ 双指针扫描、KMP、AC自动机、线性筛素数
$n \le 10^9$ $O(\sqrt n)$ 判断质数
$n \le 10^{18}$ $O(logn)$ 最大公约数,快速幂,数位DP
$n \le 10^{1000}$ $O((logn)^2)$ 高精度加减乘除
$n \le 10^{100000}$ $O(logk \times loglogk),k表示位数$ 高精度加减、FFT/NTT

Reference

0%