贪心

临项交换

临项交换法常用于推到排序类贪心的排序规则

以这道题为例:P1080 [NOIP2012 提高组] 国王游戏

交换前的贡献:\(\displaystyle max(\frac{mul}{b_i},\frac{mul* a_i}{b_{i+1}})\)
交换后的贡献:\(\displaystyle max(\frac{mul}{b_{i+1}},\frac{mul* a_{i+1}}{b_i})\)

如果满足当前最优,任意相邻两项需要满足:

\[\displaystyle max(\frac{mul}{b_i},\frac{mul* a_i}{b_{i+1}})<\displaystyle max(\frac{mul}{b_{i+1}},\frac{mul* a_{i+1}}{b_i}) \]

化简得:

\[max(b_{i+1},a_ib_i)<max(b_i,a_{i+1}b_{i+1}) \]

由于正整数,\(b_i<a_ib_i\),其余同理,那么转化为 \(a_ib_i<a_{i+1}b_{i+1}\)

于是按照 \(a_ib_i\) 排序即可


P2123 皇后游戏

一看就是升级版,式子这么高级

一样的套路,先设 \(x=c_{i-1}\)\(\displaystyle y=\sum _ {j=1}^{i-1} c_j\)

那么列不等式:

\[max(max(x,y+a_i)+b_i,y+a_i+a_{i+1})+b_{i+1})<max(max(x,y+a_{i+1})+b_{i+1},y+a_{i+1}+a_i)+b_i) \]

同时消去相同部分,剩下:

\[max(y+a_i+b_i+b_{i+1},y+a_i+a_{i+1}+b_{i+1})<max(y+a_{i+1}+b_{i+1}+b_i,y+a_{i+1}+a_i+b_i) \]

继续化简:

\[max(b_i,a_{i+1})+a_i+b_{i+1}<max(b_{i+1},a_i)+a_{i+1}+b_i \]

变形:

\[a_i+b_{i+1}-max(b_{i+1},a_i)<a_{i+1}+b_i-max(b_i,a_{i+1}) \]

等价于:

\[min(b_{i+1},a_i)\le min(b_i,a_{i+1}) \]

然后按照这个式子快乐排序然后 A 掉这道题就可以了么?当然不是~

可以发现这个状态不具有传递性

可以先进行分类讨论:

若一组内全部满足 \(a_i<b_i\),那么排序依据为 \(a_i\le a_{i+1}\)
否则,排序依据为 \(b_i\le b_{i+1}\)

那么不同组之间应该怎样排序呢?

显然第一种在第二种之前一定最优


排序相关

pro1 :

N 个区间,每个区间包含一个点,最小化点数

sol:一个和有用的套路是当区间出现包含关系时,大区间没有用,可以直接删掉,而小区间左右端点是可以分别有序的,之后进行贪心


pro2 :

N 个区间,取出最大独立集

sol1:还使用刚才的 trick,去掉较大区间,然后贪心选择线段即可
sol2:按照右端点排序,贪心取左端点


P2949 [USACO09OPEN]Work Scheduling G

按照截止日期排序后贪心选取满足条件里最大的来工作


B. 时间机器

设小区间为 \(A\),大区间为 \(B\)
贪心策略为所有区间左端点排序,扫描过程中优先匹配可以取到的大区间中右端点最小的一个,用 \(set\) 实现


类似的,把上一题中的左端点看成横坐标,右端点看成纵坐标,那么就成了 这道题


反悔贪心

P1792 [国家集训队]种树

以这道题为例,一种情境是选择是受限制的,一种物品的选择代表着另一些物品不能选
具体地来讲,这道题选择一个最大的数后,意味着相邻数不能选择,将这个数相邻的两个数之和减这个数放入堆中,表示将来可以反悔,选那两个数而不选当前数
左右相邻数这个可以用链表维护
注意一个数只可能入堆一次,所以反悔时其自己的信息可以忽略

类似的还有这个题


P4053 [JSOI2007]建筑抢修

更常见的是一个物品选择后并不是意味着具体其他哪个物品受限,而是所有物品类似于背包一样竞争体积
一般的套路是:先按照某一价值贪心,将其副价值放入堆,直到某个物品受限再去堆里寻找最劣物品进行反悔

比如这道题,扫到某个物品时如果时间够就先暂时让它被选上
如果不够,则去堆里找一个时间最长的与当前时间比较,因为每个物品价值一样,时间更短一定更优,替换掉堆里的更新时间

这道题可以说是一模一样


CF865D Buy Low Sell High

这道题略有些不常规
考虑加入有一天忽然想卖股票,那么应该找到之前的某一天与其对应地进行买股票操作
考虑反悔的体现:
比如如下数据
2 3 7
按照贪心,应该枚举到 \(3\) 的时候 \(3\)\(2\) 配对
然而到 \(7\) 的时候发现应该是 \(2\)\(7\) 配对更优
考虑这时候的答案计算式:3-2+7-3
以来一回相当于第二天没有买股票


下面的题日渐变得毒瘤了起来:

CF730I Olympiad in Programming and Sports

按照反悔贪心的套路,应该先按照一种权值贪心地选取
假定将 \(p\) 个运动最好的先选到运动队
那么一个一个选编程队即可
有两种选择方案:

  • 将一个不在运动队的人加入编程队,贡献 \(b_i\)
  • 将一个在运动队的人加入编程队,同时往运动队补一个人,贡献 \(b_i-a_i+a_j\)
    用三个堆维护 \(a_i\)\(b_i\)\(b_i-a_i\) 即可

P3045 [USACO12FEB]Cow Coupons G

一样的套路,先把券用光了,之后有两种选择:

  • 不用券购买 \(p_i\)
  • 用券购买,并且原来的一个不用券,花费 \(c_i-c_j+p_j\)

CF436E Cardboard Box

发现多了一种状态:两星
如果直接考虑两星这题就想废了
一个很妙的思想是一颗星一颗星地加
两星是怎样变来的呢?要么是一星升级来的,要么是另一个反悔来的
有三种情况:

  • 让一个关卡获得1星,花费 \(a_i\)
  • 让一个1星关卡升级为2星,花费 \(b_i-a_i\)
  • 让一个1星反悔为不选,另一个直接升级为2星,花费 \(b_i-a_j\)
  • 让一个2星反悔为1星,另一个直接升级为2星,花费 \(b_i-b_j+a_j\)

掰着指头数一数,五个堆,慢慢维护就好啦

资料: 题单


k 小值问题

一类贪心是在类似于 \(dij\)\(prim\) 中用于贪心扩展,一般题目会让求出第 \(k\) 大/小的情形,那么可以通过贪心,从数据结构(如堆)中取出一种情形后再放入和这个相关联的情形
其关键在于怎样规避同一状态下以扩展过的决策造成的影响


最小团

题意为求第 \(k\) 小团
这个题就比较典型,每次取出当前最小团后,试着将其扩大一个点,然后再放入堆中

类似套路有 这题

P2048 [NOI2010] 超级钢琴

每次的权值是一段区间和,那么可以表示为 \(sum[r]-sum[l-1]\),那么对于每个 \(l\),区间内都有唯一对应的最大的位置 \(r\) 与之对应,且这个值用 \(RMQ\) 即可求解
然后这种前 \(k\) 大的题的经典套路,开个堆,把所有三元组 \((st,l,r)\) 放入堆中,表示左端点为 \(st\),右端点在区间 \(l,r\) 的最大值,加入从堆中取出,最大值为 \(pos\),那么分裂为 \((st,l,pos-1)\)\((st,pos+1,r)\) 放入堆中


追击圣诞老人

依然是把当前候选决策放入堆中,考虑怎样快速计算下一个
考虑每次到达一个点会解锁一条链
这样来实现快速标记链上某些点不能选择呢?
其实只需要每次找到最小值的节点后把链在这个地方分成两半就好了
需要做的是快速查找链上最小值,树剖上预处理可以做到 \(log\)


背包

如果 \(l=r=1\),设计一种这样的安排方式使得后继数量可控:
按照次小减最小排序,然后一种物品一种物品来看,由于刚才的排序,使得这样可以遍历到所有情况
后继就分类讨论,有指针后移一个,到下一个物品,清空当前物品到下一个物品等
如果范围扩大,对于背包内的物品也可以类似做,即一个一个考虑每个物品,指针来回移动的实现


哈夫曼树

要回归堆的本源:从合并果子开始
其实每次拿最大的两个就是构建哈夫曼树的过程
哈夫曼树用来解决这样一个问题:构造一个 \(n\) 个叶子节点的 \(k\) 叉树,满足节点 \(\sum w_i* dep_ i\) 最小
合并果子问题正是 \(k=2\) 时的特殊情形
如果 \(k>2\) 该怎样构建呢?
还是采用类似的堆方法,只不过开始应用零补齐至叶节点完全挂满


加强版

这里的加强版显然堆过不去了,考虑用队列的小 \(trick\) 来实现


非传统套路贪心

P7078 [CSP-S2020] 贪吃蛇

看似博弈论?其实是一个很神奇的关系:如果一个蛇吃完最弱蛇后不是最弱的,那么它一定安全
为什么呢?首先是最强蛇吃最弱蛇,接着次强蛇吃次弱蛇,那么此时最强蛇一定比次弱蛇强,依次类推(这个性质也暗示了之后可以用队列优化)
那么吃完变成最弱蛇的蛇一定就不安全了么?也是不一定的
考虑如果它变成最弱蛇,之后每一个蛇吃完都变成最弱蛇,那么它们是不敢吃的
这是一个递归关系,假如递归边界的最后一个蛇不敢吃,倒数第二个就敢吃,第三个不敢吃,以此类推,与递归层数的奇偶有关
用两个双端队列模拟取最值即可


Time

一个神奇的题:
考虑如果枚举最大值位置没前途
那么不如看最小值
最小值应该放两侧,并且可以发现最小值的移动对下一个次小值是没有影响的
于是贪心往左右移动即可


首先枚举两人都喜欢的物品取 \(x\) 个,那么只有 \(A\) 喜欢的选 \(k-x\) 个,只有 \(B\) 喜欢的选 \(k-x\) 个,都不喜欢的选 \(m-x-k+x-k+x\)
这些都贪心地选取最小的几个即可
这里分成四类的思想非常妙


咕咕咕

堆模拟费用流
例题


Summary

贪心一般应用的情境:

  • 一个很明显需要先排序再 \(dp\) 或进行其他和顺序有关的算法时用临项交换推导 \(cmp\) 规则
  • 经过一系列转化变成线段或点覆盖的
  • \(dp\) 之前还是先想一下贪心,尤其是手模样例发现没有后效性的
  • 看似两个人玩游戏的走博弈论路线之前看看是不是个贪心
posted @ 2021-10-05 20:32  y_cx  阅读(43)  评论(0编辑  收藏  举报