|

贪心百法

0x00 前言

这是 zlr 在 2025/5/5 日统计的贪心常用方法,勿喷。

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

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

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

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

1.假设

我们先假设国王后面只有两个大臣,而且假设国王拿着的是 \(a_0,b_0\),第一个大臣拿的是 \(a_1,b_1\),第二个大臣拿的是 \(a_2,b_2\),我们有两种顺序。

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

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

2.推导

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

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

\[\min{(\frac{a_0\times a_1}{b_2},\frac{a_0\times a_2}{b_1})[a]} \]

的答案。

我们把 \(\frac{a_0\times a_1}{b_2}\)\(\frac{a_0\times a_2}{b_1}\) 同时除 \(a_0\),在乘上 \(b_1\times b_2\),就把这两个式子化简为 \(b_1\times a_1\)\(b_2\times a_2\)。发现什么没有?我们把结果的大小比较转化为了大臣手上数的乘积比较,如果 \(a_1\times b_1\) 小于 \(a_2\times b_2\),那么就把 1 号大臣排在前面,否则排在后面。

例题:P1248 加工生产调度

1.假设

我们假设有两个产品 \(x\)\(y\)\(x\) 产品在 A 车间加工时间是 \(a_x\),同理,其他的分别为 \(b_x\)\(a_y\)\(b_y\)

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

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

2.推导

现在我们需要比较 \(x\) 产品先做和 \(y\) 产品先做的时间,即求出

\[\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\pm \max(a,b)=\max(x\pm a,x\pm b)\),对于 \(\min\) 也成立,所以 \([b]\) 式就可以化简为:

\[\begin{array}{l} \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(a_x+b_y+\max(b_x,a_y),a_y+b_x+\max(b_y,a_x))\\ =\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(-a_y,-b_x),\max(-a_x,-b_y))+a_x+a_y+b_x+b_y\\ \end{array}\]

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

最终的排序方法是将 \(\min(a_x,a_y) < \min(b_x,b_y)\)\(x\) 排前面。

例题:P1842 奶牛玩杂技

1.假设

假设有两头牛 \(a\)\(b\),那么它们的体重分别是 \(w_a\)\(w_b\),力量是 \(s_a\)\(s_b\)

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

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

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

2.进一步的假设

如果我们要使 \(a\) 在上面,那么我们该怎么做呢?

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

易证 \(-s_a < w_b-s_a,-s_b < w_a-s_b\) 所以 \(\max(-s_a,w_a-s_b)\) 就可以变为 \(w_a-s_b\),同理 \(\max(-s_b,w_b-s_a)\) 变为 \(w_b-s_a\)

因为我们要使 \(w_a-s_b < w_b-s_a\),简单移项便可以得到 \(w_a+s_a<w_b+s_b\)

所以如果要使 \(a\) 在上面,必须要 \(w_a+s_a<w_b+s_b\)

0x02 区间类贪心(区间类问题)

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

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

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

例题:P2255 [USACO14JAN] Recording the Moolympics S

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

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

0x03 数据结构优化贪心——并查集

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

例题:U213773 家庭作业

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

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

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

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

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

0x04 数据结构优化贪心——优先队列

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

例题:[ABC407E] Most Valuable Parentheses

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

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

posted @ 2025-08-18 11:35  _q_ak  阅读(34)  评论(0)    收藏  举报