mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6mobile wallpaper 7mobile wallpaper 8mobile wallpaper 9mobile wallpaper 10mobile wallpaper 11mobile wallpaper 12mobile wallpaper 13
14594 字
38 分鐘
排序 贪心 构造
2026-06-03

第 5 章 排序、贪心与构造#

本章目标:理解排序如何暴露结构,掌握贪心算法的证明方法,并学会从不变量出发构造答案。


5.1 学习目标#

  1. 掌握常见排序算法的复杂度和稳定性。
  2. 能设计正确的比较器。
  3. 理解贪心的局部最优与全局最优。
  4. 掌握交换论证、反证法和不变量证明。
  5. 学会构造题的常见突破口。

5.2 排序的意义#

排序不是为了“排好看”,而是为了让关系变简单。

排序后常出现:

  1. 相邻元素关系。
  2. 前缀最优或后缀最优。
  3. 单调性。
  4. 可用双指针。
  5. 可用贪心选择。

例如区间合并:按左端点排序后,只需比较当前区间和最后一个合并区间。


5.3 排序算法对比#

算法平均复杂度最坏复杂度稳定性备注
冒泡排序O(n2)O(n^2)O(n2)O(n^2)稳定教学用
插入排序O(n2)O(n^2)O(n2)O(n^2)稳定小数组快
归并排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)稳定需要额外空间
快速排序O(nlogn)O(n\log n)O(n2)O(n^2)不稳定常数小
堆排序O(nlogn)O(n\log n)O(nlogn)O(n\log n)不稳定原地
计数排序O(n+V)O(n+V)O(n+V)O(n+V)可稳定值域小

5.4 比较器的严格弱序#

自定义排序比较器必须满足:

  1. 反自反:comp(x,x) 为 false。
  2. 传递性:若 x < y 且 y < z,则 x < z。
  3. 等价类传递。

错误示例:

sort(a.begin(), a.end(), [](int x, int y) {
return x <= y; // 错误:x == y 时也返回 true
});

正确写法:

return x < y;

5.5 贪心算法#

贪心每一步做当前看起来最好的选择。但“看起来最好”不代表真的对。

贪心要证明:

  1. 贪心选择性质:存在一个最优解包含当前选择。
  2. 最优子结构:做出选择后,剩余问题仍是同类最优问题。

5.6 交换论证#

例:活动选择问题。给定若干区间,选择最多互不重叠区间。

贪心:每次选结束时间最早的区间。

证明:设最优解中第一个选择为 A,贪心选择为 G。由于 G 的结束时间不晚于 A,用 G 替换 A 不会减少后续可选区间数量。因此存在一个包含 G 的最优解。

这说明贪心第一步安全,后续递归同理。


5.7 常见贪心模型#

5.7.1 区间调度#

  • 按结束时间排序。
  • 能选就选。
  • 目标是数量最多。

5.7.2 区间覆盖#

  • 按左端点排序。
  • 在所有能接上的区间中选右端点最远的。
  • 目标是用最少区间覆盖目标区间。

5.7.3 Huffman 合并#

每次合并最小的两个权值。

总成本:

merge cost\sum \text{merge cost}

用小根堆实现,复杂度 O(nlogn)O(n\log n)


5.8 构造题#

构造题要求输出一个满足条件的对象,而不是最优值。

常见突破口:

  1. 奇偶性。
  2. 模运算余数。
  3. 排列位置。
  4. 图的度数。
  5. 不变量。
  6. 从小规模样例找规律。

构造题的关键是保证每一步都不破坏条件。


5.9 易错点#

  1. 贪心没有证明,只靠直觉。
  2. 排序比较器写成非严格关系。
  3. 区间开闭端点处理不一致。
  4. Huffman 合并忘记用 long long。
  5. 构造题只考虑样例,没有覆盖小规模所有情况。
  6. 忘记证明构造结果一定存在或说明无解条件。

5.10 练习#

  1. 证明活动选择的贪心正确性。
  2. 实现区间合并和区间覆盖。
  3. 用堆实现 Huffman 合并成本。
  4. 找一个贪心失败的反例,并解释为什么失败。
  5. 构造一个长度为 n 的排列,使相邻差满足指定条件。

ACM/OI 扩展:贪心证明与构造套路#

本节补充 OIer 和 ACMer 常用的算法套路、模板、复杂度分析和易错点。 写模板时默认使用 C++17,变量名尽量短,但注释要说明不变量和适用条件。

1. 排序后贪心通用证明#

很多贪心题先排序,再做局部选择。题解要写清排序依据。

若交换 a,b 前后总代价比较为 CabCba, 则 a 应在 b 前。\text{若交换 } a,b \text{ 前后总代价比较为 } C_{ab} \le C_{ba}, \text{ 则 } a \text{ 应在 } b \text{ 前。}

要点

  • 若按结束时间排序,通常为了给后续留更多空间。
  • 若按差值排序,通常来自交换相邻元素的收益比较。
  • 若按比值排序,要注意交叉相乘避免浮点误差。

2. 区间覆盖模板#

用最少区间覆盖目标 [L,R]。

sort(seg.begin(), seg.end());
int i = 0, ans = 0;
while (L < R) {
int far = L;
while (i < n && seg[i].l <= L) {
far = max(far, seg[i].r);
++i;
}
if (far == L) { ans = -1; break; }
L = far; ++ans;
}

要点

  • 每次在能接上的区间中选右端点最远。
  • 排序按左端点升序。

常见坑

  • 闭区间和半开区间要统一。

3. 差值排序模型#

两种选择 A/B,每个对象放一边,常按贡献差排序。

Δi=costAicostBi\Delta_i = costA_i - costB_i

要点

  • 如果必须选 k 个放 A,通常选 Δi\Delta_i 最小的 k 个。
  • 如果目标是最大收益,则按收益差反向排序。

4. 构造题常用不变量#

构造题应先找必须保持的性质。

要点

  • 奇偶性:总和奇偶、位置奇偶、颜色奇偶。
  • 模数:按余数分组构造。
  • 图论度数:度数和必须为偶数。
  • 排列:从小规模 n=1..8 暴力找规律。
  • 贪心补位:每一步选择不破坏剩余可行性。

训练题型

  • 构造一个排列使相邻和不为质数。
  • 构造括号序列满足前缀和非负。

5. Huffman 合并模板#

每次合并最小两项,常见于最优合并代价。

priority_queue<long long, vector<long long>, greater<long long>> pq;
for (auto x : a) pq.push(x);
long long ans = 0;
while (pq.size() > 1) {
long long x = pq.top(); pq.pop();
long long y = pq.top(); pq.pop();
ans += x + y;
pq.push(x + y);
}

要点

  • 复杂度 O(nlogn)O(n\log n)
  • 权值和可能超过 int。

本章模板背诵清单#

  • 能在 5 分钟内写出本章第一个核心模板。
  • 能说出每个模板的时间复杂度和空间复杂度。
  • 能说明模板不适用的反例。
  • 能为模板构造 3 个边界样例。
  • 能把模板改成 0-index 或 1-index 版本。

构造与贪心题库套路#

邻项交换法#

排序规则常通过比较 AB 和 BA 两种相邻顺序得到。

A before B    cost(A,B)cost(B,A)A \text{ before } B \iff cost(A,B) \le cost(B,A)
  • 国王游戏、排队打水、最小字典序拼接常用。
  • 比较乘法时注意溢出,用 __int128。

反悔贪心#

先贪心选择,后面发现更优时用堆反悔。

  • 课程安排:按结束时间排序,用堆维护已选课程耗时。
  • 项目选择:可行集合中取收益最大。
  • 本质是维护当前选择集合的最优性。

构造排列#

排列构造常用奇偶分组、循环移位、从两端取数。

  • 避免相邻差为 1:先输出奇数再输出偶数。
  • 字典序最小:从左到右确定每位。
  • 满足 mex 条件:围绕缺失值构造。

博弈构造#

简单博弈先找必败态和转移关系。

win(x)=ynext(x), win(y)=falsewin(x)=\exists y \in next(x),\ win(y)=false
  • Nim 异或和为 0 是必败态。
  • 构造取法时让异或和变为 0。

贪心反例检查#

写完贪心后强制找反例。

  • 局部最优是否可能影响未来选择?
  • 是否存在两个选择当前一样好但未来不同?
  • 能否用交换论证把任意最优解变成贪心解?

高频模板训练卡#

这一组训练卡用于把“05_排序_贪心_构造”转化为赛场识别能力。每张卡都包含题目信号、模板选择、复杂度、反例和实现检查。

卡 1:区间调度#

  • 题目信号:题目要求最少/最多/构造且出现 区间调度 模型。
  • 优先模板:先尝试 区间调度,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 区间调度 写一个反例检查和一个正确性证明。

卡 2:区间覆盖#

  • 题目信号:题目要求最少/最多/构造且出现 区间覆盖 模型。
  • 优先模板:先尝试 区间覆盖,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 区间覆盖 写一个反例检查和一个正确性证明。

卡 3:反悔贪心#

  • 题目信号:题目要求最少/最多/构造且出现 反悔贪心 模型。
  • 优先模板:先尝试 反悔贪心,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 反悔贪心 写一个反例检查和一个正确性证明。

卡 4:Huffman 合并#

  • 题目信号:题目要求最少/最多/构造且出现 Huffman 合并 模型。
  • 优先模板:先尝试 Huffman 合并,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 Huffman 合并 写一个反例检查和一个正确性证明。

卡 5:邻项交换排序#

  • 题目信号:题目要求最少/最多/构造且出现 邻项交换排序 模型。
  • 优先模板:先尝试 邻项交换排序,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 邻项交换排序 写一个反例检查和一个正确性证明。

卡 6:差值排序#

  • 题目信号:题目要求最少/最多/构造且出现 差值排序 模型。
  • 优先模板:先尝试 差值排序,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 差值排序 写一个反例检查和一个正确性证明。

卡 7:最小字典序构造#

  • 题目信号:题目要求最少/最多/构造且出现 最小字典序构造 模型。
  • 优先模板:先尝试 最小字典序构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 最小字典序构造 写一个反例检查和一个正确性证明。

卡 8:奇偶构造#

  • 题目信号:题目要求最少/最多/构造且出现 奇偶构造 模型。
  • 优先模板:先尝试 奇偶构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 奇偶构造 写一个反例检查和一个正确性证明。

卡 9:mex 构造#

  • 题目信号:题目要求最少/最多/构造且出现 mex 构造 模型。
  • 优先模板:先尝试 mex 构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 mex 构造 写一个反例检查和一个正确性证明。

卡 10:括号构造#

  • 题目信号:题目要求最少/最多/构造且出现 括号构造 模型。
  • 优先模板:先尝试 括号构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 括号构造 写一个反例检查和一个正确性证明。

卡 11:图度数构造#

  • 题目信号:题目要求最少/最多/构造且出现 图度数构造 模型。
  • 优先模板:先尝试 图度数构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 图度数构造 写一个反例检查和一个正确性证明。

卡 12:排列构造#

  • 题目信号:题目要求最少/最多/构造且出现 排列构造 模型。
  • 优先模板:先尝试 排列构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 排列构造 写一个反例检查和一个正确性证明。

卡 13:Nim 博弈#

  • 题目信号:题目要求最少/最多/构造且出现 Nim 博弈 模型。
  • 优先模板:先尝试 Nim 博弈,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 Nim 博弈 写一个反例检查和一个正确性证明。

卡 14:堆维护可选集#

  • 题目信号:题目要求最少/最多/构造且出现 堆维护可选集 模型。
  • 优先模板:先尝试 堆维护可选集,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 堆维护可选集 写一个反例检查和一个正确性证明。

卡 15:排序 + 双指针#

  • 题目信号:题目要求最少/最多/构造且出现 排序 + 双指针 模型。
  • 优先模板:先尝试 排序 + 双指针,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 排序 + 双指针 写一个反例检查和一个正确性证明。

卡 16:排序 + 前缀最值#

  • 题目信号:题目要求最少/最多/构造且出现 排序 + 前缀最值 模型。
  • 优先模板:先尝试 排序 + 前缀最值,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 排序 + 前缀最值 写一个反例检查和一个正确性证明。

卡 17:贪心 + 证明#

  • 题目信号:题目要求最少/最多/构造且出现 贪心 + 证明 模型。
  • 优先模板:先尝试 贪心 + 证明,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 贪心 + 证明 写一个反例检查和一个正确性证明。

卡 18:局部调整#

  • 题目信号:题目要求最少/最多/构造且出现 局部调整 模型。
  • 优先模板:先尝试 局部调整,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 局部调整 写一个反例检查和一个正确性证明。

卡 19:极端值优先#

  • 题目信号:题目要求最少/最多/构造且出现 极端值优先 模型。
  • 优先模板:先尝试 极端值优先,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 极端值优先 写一个反例检查和一个正确性证明。

卡 20:分组构造#

  • 题目信号:题目要求最少/最多/构造且出现 分组构造 模型。
  • 优先模板:先尝试 分组构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 分组构造 写一个反例检查和一个正确性证明。

卡 21:按余数构造#

  • 题目信号:题目要求最少/最多/构造且出现 按余数构造 模型。
  • 优先模板:先尝试 按余数构造,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 按余数构造 写一个反例检查和一个正确性证明。

卡 22:构造无解判定#

  • 题目信号:题目要求最少/最多/构造且出现 构造无解判定 模型。
  • 优先模板:先尝试 构造无解判定,再写交换论证或不变量。
  • 核心不变量:每一步选择后,剩余问题仍然存在某个最优解。
  • 复杂度目标:排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。
  • 不适用场景:存在未来约束但当前选择没有证明安全时不适用。
  • 实现检查:检查比较器严格弱序、相等情况、无解条件。
  • 边界样例:n=1/2、全相等、极端重复、刚好无解。
  • 训练题型:为 构造无解判定 写一个反例检查和一个正确性证明。

本章赛前默写任务#

  • 默写 区间调度 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 区间覆盖 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 反悔贪心 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 Huffman 合并 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 邻项交换排序 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 差值排序 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 最小字典序构造 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 奇偶构造 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 mex 构造 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 默写 括号构造 的模板,并写出 排序 O(n log n),堆贪心 O(n log n),构造多为 O(n)。。
  • 为本章任选 3 个模板各写一个最小输入。
  • 为本章任选 3 个模板各写一个会使错误实现失败的反例。
  • 把本章所有模板整理到个人代码库,并标注 0-index / 1-index。

贪心与构造的赛场自检表#

  • 我是否能写出局部选择为什么安全?
  • 我是否比较过相邻两个元素交换前后的代价?
  • 我是否检查了 n=1、n=2、全相等、极端重复?
  • 我是否明确了无解条件,而不是默认一定有解?
  • 我是否避免了非严格比较器,例如 return a <= b
  • 我是否用 long long 或 __int128 处理乘法比较?
  • 我是否能构造一个反例说明错误贪心为什么错?
  • 我是否把输出结果重新代入题目条件验证?
  • 如果答案有多种,我是否知道题目要求任意一种还是字典序最小?
  • 如果按余数或奇偶构造,我是否覆盖了所有余数类?

贪心题反例生成建议#

  • 对每一种贪心规则,都尝试构造“当前最优但未来变差”的样例。
  • 对排序贪心,枚举 3 到 6 个元素的所有排列,比较贪心答案和暴力答案。
  • 对区间贪心,重点测试端点相等、完全包含、刚好相接、完全不相交。
  • 对堆反悔贪心,重点测试后出现的大收益元素能否替换前面选择。
  • 对构造题,先暴力枚举小 n 的所有合法答案,从合法答案中观察模式。
  • 如果小 n 无解,大 n 有解,必须把分界条件写进题解。
  • 如果构造依赖奇偶性,必须分别检查奇数 n 和偶数 n。
  • 如果构造依赖模数,必须覆盖所有余数类。
  • 如果答案要求字典序最小,普通“任意构造”往往不够。
  • 构造完成后,写一个 checker 验证输出是否满足题目条件。

05_排序_贪心_构造:详细训练清单#

本清单用于把章节知识拆成可执行的小任务,偏 OI/ACM 赛场使用。

训练点 1:稳定排序#

  • 识别信号:题面出现与“稳定排序”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“稳定排序”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 2:比较器#

  • 识别信号:题面出现与“比较器”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“比较器”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 3:邻项交换#

  • 识别信号:题面出现与“邻项交换”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“邻项交换”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 4:区间调度#

  • 识别信号:题面出现与“区间调度”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“区间调度”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 5:区间覆盖#

  • 识别信号:题面出现与“区间覆盖”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“区间覆盖”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 6:反悔贪心#

  • 识别信号:题面出现与“反悔贪心”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“反悔贪心”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 7:Huffman 合并#

  • 识别信号:题面出现与“Huffman 合并”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“Huffman 合并”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 8:堆维护可选集#

  • 识别信号:题面出现与“堆维护可选集”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“堆维护可选集”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 9:差值排序#

  • 识别信号:题面出现与“差值排序”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“差值排序”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 10:字典序构造#

  • 识别信号:题面出现与“字典序构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“字典序构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 11:奇偶构造#

  • 识别信号:题面出现与“奇偶构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“奇偶构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 12:mex 构造#

  • 识别信号:题面出现与“mex 构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“mex 构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 13:排列构造#

  • 识别信号:题面出现与“排列构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“排列构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 14:博弈构造#

  • 识别信号:题面出现与“博弈构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“博弈构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 15:无解判定#

  • 识别信号:题面出现与“无解判定”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“无解判定”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 16:checker 验证#

  • 识别信号:题面出现与“checker 验证”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“checker 验证”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 17:稳定排序#

  • 识别信号:题面出现与“稳定排序”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“稳定排序”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 18:比较器#

  • 识别信号:题面出现与“比较器”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“比较器”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 19:邻项交换#

  • 识别信号:题面出现与“邻项交换”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“邻项交换”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 20:区间调度#

  • 识别信号:题面出现与“区间调度”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“区间调度”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 21:区间覆盖#

  • 识别信号:题面出现与“区间覆盖”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“区间覆盖”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 22:反悔贪心#

  • 识别信号:题面出现与“反悔贪心”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“反悔贪心”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 23:Huffman 合并#

  • 识别信号:题面出现与“Huffman 合并”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“Huffman 合并”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 24:堆维护可选集#

  • 识别信号:题面出现与“堆维护可选集”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“堆维护可选集”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 25:差值排序#

  • 识别信号:题面出现与“差值排序”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“差值排序”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 26:字典序构造#

  • 识别信号:题面出现与“字典序构造”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“字典序构造”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。
分享

如果這篇文章對你有幫助,歡迎分享給更多人!

排序 贪心 构造
https://lemusakuya.com/posts/study-notes/algorithm/05_排序_贪心_构造/
作者
レム・咲く夜
發布於
2026-06-03
許可協議
CC BY-NC-SA 4.0

部分資訊可能已經過時

目錄