1886 字
9 分钟
贪心算法总结

1 用两个元素的交换来推出贪心方法(邻项交换法)#

这个方法适用于解决顺序问题或者安排问题,通过先解决小问题来总结出规律,从而解决出这个题目。

例题:P1080 [NOIP 2012 提高组] 国王游戏#

这个题目就是典型的邻项交换法推贪心。

1. 假设#

我们先假设国王后面只有两个大臣,而且假设国王拿着的是 a0,b0a_0,b_0,第一个大臣拿的是 a1,b1a_1,b_1,第二个大臣拿的是 a2,b2a_2,b_2,我们有两种顺序。

  • 第一个大臣在前面,那么第一个大臣得到的奖金就是 a0b1\frac{a_0}{b_1},第二个大臣拿到的奖金就是 a0×a1b2\frac{a_0\times a_1}{b_2},最高的奖赏就是 max(a0b1,a0×a1b2)\max{(\frac{a_0}{b_1},\frac{a_0\times a_1}{b_2})}

  • 第二个大臣在前面,那么第二个大臣得到的奖金就是 a0b2\frac{a_0}{b_2},第一个大臣拿到的奖金就是 a0×a2b1\frac{a_0\times a_2}{b_1},最高的奖赏就是 max(a0b2,a0×a2b1)\max{(\frac{a_0}{b_2},\frac{a_0\times a_2}{b_1})}

2. 推导#

现在我们就要来比较最高的奖赏,即比较 max(a0b2,a0×a2b1)\max{(\frac{a_0}{b_2},\frac{a_0\times a_2}{b_1})}max(a0b1,a0×a1b2)\max{(\frac{a_0}{b_1},\frac{a_0\times a_1}{b_2})} 谁更小。

我们发现 a0b1\frac{a_0}{b_1} 一定小于等于 a0×a2b1\frac{a_0\times a_2}{b_1}(因为 a2a_2 是大于 0 的整数,即 a21a_2 \ge 1,不会变小),同理 a0b2\frac{a_0}{b_2} 一定小于等于 a0×a1b2\frac{a_0\times a_1}{b_2}。所以我们可以将式 [a][a] 转化为求 min(a0×a1b2,a0×a2b1)[a]\min{(\frac{a_0\times a_1}{b_2},\frac{a_0\times a_2}{b_1})[a]} 的答案。

我们把 a0×a1b2\frac{a_0\times a_1}{b_2}a0×a2b1\frac{a_0\times a_2}{b_1} 同时除 a0a_0,在乘上 b1×b2b_1\times b_2,就把这两个式子化简为 b1×a1b_1\times a_1b2×a2b_2\times a_2。发现什么没有?我们把结果的大小比较转化为了大臣手上数的乘积比较,如果 a1×b1a_1\times b_1 小于 a2×b2a_2\times b_2,那么就把 1 号大臣排在前面,否则排在后面。

例题:P1248 加工生产调度#

1. 假设#

我们假设有两个产品 xxyyxx 产品在 A 车间加工时间是 axa_x,同理,其他的分别为 bxb_xaya_ybyb_y

  • xx 产品先做,那么 xx 产品所有的时间就是 ax+bxa_x+b_x,由于 yy 产品要等到 xx 产品加工完才行,那么 yy 产品在 A 车间加工就需要 ax+aya_x+a_y 的时间(axa_x 的等待时间),而此时 xx 产品在 B 车间加工了 aya_y 的时间,还需要 max(bxay,0)\max{(b_x-a_y,0)} 的时间加工,所以 yy 产品在 B 车间需要 max(bxay,0)+by\max{(b_x-a_y,0)}+b_y 的时间加工。由于 yy 产品肯定是等到 xx 产品加工后才结束,所以花费的总时间是 ax+ay+max(bxay,0)+bya_x+a_y+\max{(b_x-a_y,0)}+b_y 的时间。

  • 同理 yy 产品先做就需要花费 ax+ay+max(byax,0)+bxa_x+a_y+\max{(b_y-a_x,0)}+b_x 的时间。

2. 推导#

现在我们需要比较 xx 产品先做和 yy 产品先做的时间,即求出 min(ax+ay+max(bxay,0)+by,ax+ay+max(byax,0)+bx)[b]\min(a_x+a_y+\max(b_x-a_y,0)+b_y,a_x+a_y+\max(b_y-a_x,0)+b_x)[b]

不难发现 x±max(a,b)=max(x±a,x±b)x\pm \max(a,b)=\max(x\pm a,x\pm b),对于 min\min 也成立,所以 [b][b] 式就可以化简为:

min(ax+ay+max(bx,ay)ay+by,ax+ay+max(by,ax)ax+bx)\min(a_x+a_y+\max(b_x,a_y)-a_y+b_y,a_x+a_y+\max(b_y,a_x)-a_x+b_x)

=min(ax+by+max(bx,ay),ay+bx+max(by,ax))=\min(a_x+b_y+\max(b_x,a_y),a_y+b_x+\max(b_y,a_x))

=min(max(bx,ay)aybx,max(by,ax)axby)+ax+ay+bx+by=\min(\max(b_x,a_y)-a_y-b_x,\max(b_y,a_x)-a_x-b_y)+a_x+a_y+b_x+b_y

=min(max(ay,bx),max(ax,by))+ax+ay+bx+by=\min(\max(-a_y,-b_x),\max(-a_x,-b_y))+a_x+a_y+b_x+b_y

至于继续的推导,需要用到 Johnson 定理,感兴趣的读者可以自己去了解一下。

最终的排序方法是将 min(ax,ay)<min(bx,by)\min(a_x,a_y) < \min(b_x,b_y)xx 排前面。

例题:P1842 奶牛玩杂技#

1. 假设#

假设有两头牛 aabb,那么它们的体重分别是 waw_awbw_b,力量是 sas_asbs_b

如果现在只有这两头牛,那么:

  • aa 在上面,那么 aa 的压扁指数为 sa-s_abb 的压扁指数为 wasbw_a-s_b,总压扁指数为 max(sa,wasb)\max(-s_a,w_a-s_b)。(注意这里不能直接省去 sa-s_a,虽然它为负数,但当 wasb<saw_a-s_b<-s_a 时就会取 sa-s_a。)

  • bb 在上面,那么同理,总压扁指数为 max(sb,wbsa\max(-s_b,w_b-s_a)。

2. 进一步的假设#

如果我们要使 aa 在上面,那么我们该怎么做呢?

我们需要满足 max(sa,wasb)<max(sb,wbsa)\max(-s_a,w_a-s_b) < \max(-s_b,w_b-s_a)

易证 sa<wbsa,sb<wasb-s_a < w_b-s_a,-s_b < w_a-s_b 所以 max(sa,wasb)\max(-s_a,w_a-s_b) 就可以变为 wasbw_a-s_b,同理 max(sb,wbsa)\max(-s_b,w_b-s_a) 变为 wbsaw_b-s_a

因为我们要使 wasb<wbsaw_a-s_b < w_b-s_a,简单移项便可以得到 wa+sa<wb+sbw_a+s_a<w_b+s_b

所以如果要使 aa 在上面,必须要 wa+sa<wb+sbw_a+s_a<w_b+s_b

2 区间类贪心(区间类问题)#

这个方法适用于一系列的区间取舍问题,最基础的问题有:

  1. 区间选点(在 nn 个区间放尽量少的点,使得每个区间都有点)
  2. 区间覆盖(选择尽量少的区间覆盖整个线段)
  3. 区间分组(将区间分成尽量少的组使得组内的区间两两互不相交)

区间类问题的常用方法是按右端点排序,方便解决区间取舍后对下一个区间的影响(如果不按右端点排序就有后效性了,不能用贪心解决)。

例题:P2255 [USACO14JAN] Recording the Moolympics S#

同样,我们先按右端点排序,然后因为有两个节目可以同时录制,我们用 p1p_1p2p_2 来代表两个节目的结束时间。

贪心策略:由于靠前的节目是结束时间更小的,所以结束后可以更快安排下一个节目,所以我们只需要按右端点排序后挨个判断节目是否可以即可。

例题:P11232 [CSP-S 2024] 超速检测#

本题的前一部分求超速区间这里不再细讲了。

对于每一个区间 [l1,r1][l_1,r_1],如果另一个区间 [l2,r2][l_2,r_2] 使得 l2l1r1r2l_2 \le l_1 \le r_1 \le r_2,也就是区间 [l1,r1][l_1,r_1] 被包含在了 [l2,r2][l_2,r_2] 里,那么显然满足了 [l1,r1][l_1,r_1] 就一定满足 [l2,r2][l_2,r_2],且根据贪心策略,满足 [l1,r1][l_1,r_1] 是最优的。

所以我们按左端点排序,找到下标 x,yx,y 满足 rx>ryr_x > r_y,那么将 ryr_y 删除,最后剩下的区间是一定不会两两重叠的。

最后按普通的贪心(用 lower_bound 找离区间右端点最近的摄像头是最优的)即可完成。

注意精度误差带来的影响。

3 数据结构优化贪心——并查集#

在有些时候,贪心的复杂度为 n2n^2,其中一个 nn 的复杂度是寻找前面满足条件的元素所需的复杂度,所以我们可以用并查集优化。

例题:U213773 家庭作业#

我们很容易推出贪心策略:先选得分大的,如果这个作业有时间就做,没时间直接舍去,但由于每次查找之前的时间都需要 nn,总复杂度为 n2n^2,考虑并查集优化。

我们定义 fif_iii 天前的第一个空闲时间,那么查找这个作业是否可以就是 find(x)find(x),复杂度为 11(路径压缩)。

例题:P1525 [NOIP 2010 提高组] 关押罪犯#

很明显,我们需要让危害性较大的两个罪犯分到两个监狱里。我们用 fif_i 表示第 ii 个人和 fif_i 个人放在一个监狱里,bib_i 表示跟 ii 仇恨度最大的人是谁。

很明显,如果现在 bxb_x 已经有值了,所以为了他们并不互相冲突,只能将 bxb_xyy 放在一个监狱里面,如果没有 bxb_x,根据贪心策略,肯定将 xxyy 放在两个监狱里。

4 数据结构优化贪心——优先队列#

贪心中查找最大或最小值且两个元素在同一位置的查找范围一样,那么就可以用 logn\log n 的复杂度取代 nn 的暴力枚举。

例题:[ABC407E] Most Valuable Parentheses#

很明显,我们只要满足左括号的数量等于右括号的数量且在任何一个 1i1\sim i 的序列中的左括号都要大于右括号,所以我们只需要在前 i×21i \times 2 - 1 个当中选择 ii 个,可以证明保证从 2×x12\times x - 12×(x+1)12 \times (x + 1) - 1 会同时增加一个左括号和右括号。

所以我们可以用优先队列,从 11 枚举到 2×n2\times n,每次枚举到奇数位就将序列中最大的元素累加答案。

贪心算法总结
https://github.com/FrankWkd-Pro/Firefly
作者
FrankWkd
发布于
2025-10-19
许可协议
CC BY-NC-SA 4.0
上次编辑日期: 2025-10-19

部分信息可能已经过时

目录