贪心基本功:排序、双指针与区间模型

T1 送分题的主战场:把问题排个序,答案往往就浮出来了

一、贪心的使用纪律

贪心每步取眼前最优、不做回溯,正确性无法靠直觉保证。考场纪律:

  1. 先猜策略:常见排序关键字只有几种——右端点、截止时间、差值、比值、大小。逐个试。
  2. 小样例验证:手算 2~3 组小数据,策略错会立刻暴露。
  3. 想交换论证:心里过一遍"交换相邻两个元素不会更优"的证明;说不清楚就慎用。
  4. 拿不准就加反悔机制:见反悔贪心。
T1 考情:近年 T1 都是"排序 + 一个关键观察":2024 决斗(双指针)、2025 社团招新(反悔)、2021 廊桥分配(贪心观察)。纯模拟送分时代已过,但套路就这几种。

二、双指针:排序后的免费午餐

数组有序后,很多"找一对 / 找一段"的问题可以用两个指针 O(n) 扫完:

例(2024 决斗 P11231):n 只怪兽,第 i 只攻击力 ai。每回合可让一只怪兽攻击另一只:攻击力严格大的一方消灭对方(相等则无法消灭);每只怪兽至多消灭一只、也至多只被消灭一次。求最少存活几只。

这等价于"最多能配成多少对(强者,弱者)"。排序后双指针:让最弱的未配对怪兽去被"能赢它的最弱怪兽"消灭,能配就配——每只怪兽只扫一次。答案 = n − 配对数,O(n log n) 搞定。

生活类比:掰手腕淘汰赛,每人最多赢一场、输一场。想淘汰最多人,就不能让冠军去打亚军(浪费!),而要让"刚好能赢的人"去赢——大力士要省着用。
已排序攻击力 [1, 2, 3, 4, 5, 6]:左边指针找"被淘汰者",右边指针找"能赢它的最弱者" 1 被淘汰 2 被淘汰 3 被淘汰 4 胜 5 胜 6 胜 配成 3 对 → 淘汰 3 只 存活 = 6 − 3 = 3 4 是最弱的能赢 1 的、5 赢 2、6 赢 3——绝不浪费更强的怪兽
决斗的双指针配对:淘汰数最大 = 配对数最大
排序 + 双指针的识别信号:答案只关心"配对方案"而不关心顺序;数据范围 n ≤ 2×10⁵

三、区间模型三姐妹

以下三个模型是贪心题的"常驻嘉宾",差别只在问法:

模型问题贪心策略
区间调度最多选多少个互不重叠的区间按右端点排序,能选就选
区间选点最少选几个点,使每个区间都含至少一个点按右端点排序,当前区间还没被覆盖就在其右端点放点
区间覆盖最少选几个区间覆盖目标线段 [A, B]每次在"左端点 ≤ 当前位置"的区间里选右端点最远的
区间选点:按右端点排序后扫描,黑点为所选的点(3 个点覆盖全部 5 个区间) 区间1右端点 区间3右端点 区间5右端点
"能复用就不加新点":每个点都放在当前未覆盖区间的右端点上,向右覆盖能力最强

真题对照:2024 超速检测 = 区间选点;P1514 引水入城第二问 = 区间覆盖。两者常与 BFS / 模拟复合出现(先求出区间,再贪心)。

四、模板代码(区间选点)

sort(seg + 1, seg + n + 1, [](Seg a, Seg b){ return a.r < b.r; });
int ans = 0; long long last = LLONG_MIN;   // 上一个选的点
for (int i = 1; i <= n; i++)
    if (seg[i].l > last) {                 // 当前区间还没被盖住
        ans++; last = seg[i].r;            // 在右端点放一个新点
    }

五、易错点清单

六、练习

配套小测(5 题,即时判分)