反悔贪心

算法简介

反悔贪心是对贪心策略的一种优化有些时候贪心策略是错的但如果可以撤销之前的操作那么就会变为正确的

反悔贪心主要有两种一种是反悔堆即将之前没有选择的操作加入一个堆中保证每次取出的堆顶都是当前没有选择的策略中最优的若由于当前操作则将其替换还有一种是反悔自动机可以设计一个自动机来决策当前的操作

另外有一种观点认为反悔贪心的本质是贪心模拟费用流我认为是有一定道理的这种观点也有利于解释反悔贪心的正确性

经典例题

[USACO09OPEN] Work Scheduling G

反悔贪心板子题

按任务按截止时间排序若当前这个工作做得了即已经做的件数小于这个工作的截止日期那么直接做并把它的价值放进优先队列中若当前这个工作没时间做了我们发现如果把前面一个工作去除这个工作就做得了于是对比小根堆顶的价值与他的价值谁大就保留谁

时间复杂度 $\Theta(n\log n)$

[JSOI2007] 建筑抢修

反悔贪心板子题 $\times 2$

我们发现每个建筑的贡献都是一样的按建筑的 $T2$ 排序一个个枚举若当前建筑可以修即之前已经选的建筑的 $T1$ 之和加上当前这个建筑的 $T1$ 不大于当前建筑的 $T2$那么就直接将这个建筑的 $T1$ 加上并放进优先队列并把答案 $+1.$ 若不能修考虑到价值一样的情况下最小化总修理时间那么将大根堆的堆顶取出若其大于当前点的 $T1$更新花费总时间

时间复杂度 $\Theta(n\log n)$

[P2107] 小 Z 的 AK 计划

反悔贪心板子题 $\times 3$

首先按机房位置升序考虑假设你有一种到了机房就必须 $\rm AK$ 的欲望实际上是因为如果不 $\rm AK$ 这个机房就没必要走到这里而是在之前停下那么如果当前的总用时加上 $\rm AK$ 这个机房的用时不到 $m$ 的时候你就可以直接 $\rm AK$并将这个机房的 $\rm AK$ 用时加入大根堆若当前时间不够就不断弹出堆顶直到剩下的时间足够 $\rm AK.$ 一边枚举一边记录当前最优答案即可

时间复杂度 $\Theta(n\log n)$

[CF865D] Buy Low Sell High

首先考虑一个贪心对每个数选择它后面第一个比他大并且还没被选择的数容易发现这样是错的$\rm Hack$ 数据如下

4
1 2 3 4

于是我们考虑给上面那个贪心加上反悔操作每次将当前值放入小根堆中若当前枚举的这个数大于小根堆堆顶那么就将其卖掉获得 a[i] - Q.top() 的贡献同时为了之后能够反悔我们还要将它也加入堆中我们发现如果后面还有一个数将其卖出那么其等价于直接将此时的堆顶卖出这样一来如果我们认为某个点要卖出那么它就要入堆两次

时间复杂度 $\Theta(n\log n)$

[国家集训队] 种树

考虑选了一个数以后会发生什么它两边的数都不能选了于是考虑在选了一个点之后把它两边的数标为不能选然后在原处加入一个权值为 $a_{i-1}+a_{i+1}-a_i$ 的点每次取最大的点若已标为不能选就跳过否则选他然后将两边的点删去更新链表顺序

容易证明这样做是对的并且可以用优先队列维护时间复杂度 $\Theta(n\log n)$