C++ 竞赛入门
数据范围与复杂度直觉
先看数据范围,再决定循环层数。
数据范围与复杂度直觉
本节学会什么
- 从题目数据范围估计可接受的循环层数
- 区分线性扫描和两层循环的工作量
- 在写代码前写下预期的时间复杂度
为什么需要它
同一题的答案可能既能用一层循环求出,也能用三层循环“硬算”出来。小样例看不出差别,评测数据一大,后者就会 TLE。数据范围是题目给你的重要提示:它告诉你程序最多能做多少次近似操作。
最小可运行示例
读入 n 个数并求最大值,只需要每个数看一次:
int maximum = a[0];
for (int i = 1; i < n; i += 1) {
if (a[i] > maximum) {
maximum = a[i];
}
}
这段循环大约执行 n 次,记作 O(n)。若要把每一对元素都比较一次,就会有两层循环:
for (int i = 0; i < n; i += 1) {
for (int j = i + 1; j < n; j += 1) {
// 比较 a[i] 和 a[j]
}
}
它大约执行 n * n / 2 次,记作 O(n^2)。当 n = 100000 时,两者的数量级差异已经非常大。
关键过程与易错点
n <= 20时,可以考虑O(2^n)的枚举;这类做法会在后面的搜索中出现。n <= 10^3时,通常可以考虑O(n^2)的两层循环。n <= 10^5时,通常需要O(n)或O(n log n)的方法,而不是双重循环。- 这些是估算,不是硬性定律。测试组数、每次循环中的工作量、常数和机器速度都会影响实际时间。
- 看范围时不要只看单个
n:若有T组数据,应一起估算T次处理的总工作量。 - 写代码前先在草稿上标出主要循环,并写下预期复杂度。代码完成后再检查它是否与范围相配。
动手检查
分别估算下列循环的执行次数:n = 1000 的两层完整循环,以及 n = 100000 的一层循环。无需精确到每一步,只要比较数量级。
找一道已经做过的数组题,在题面上圈出最大数据范围。写下自己的程序有几层与 n 有关的循环,以及它大约是 O(n) 还是 O(n^2)。
立即练习
| 难度 | 题目 | 题解 | 训练目标 |
|---|---|---|---|
| 基础 | 洛谷 P1059:明明的随机数 | 题解 | 先估计一遍扫描和去重处理的工作量。 |
| 巩固 | 洛谷 P1428:小鱼比可爱 | 题解 | 在编码前写下 O(n^2),再用题目范围判断它为何可行。 |