【学习笔记】贪心学习笔记。
主要写点方式方法然后记点题目。
参考资料
贪心还能这么玩?——浅谈进阶贪心 \(^1\)
关于 P2123 【皇后游戏】错解的一些讨论以及一种不同的做法 \(^2\)
浅谈邻项交换排序的应用以及需要注意的问题 \(^3\)
学长的反悔贪心课件。
邻项交换法
比较基础的方法,主要就是证明一个状态是最优或是最劣,通过交换两项然后比较交换前后方案的优劣,构造与答案有关的不等式,最终推到全局该如何选取方案。
不过在推导不等式的时候,一定要满足严格弱序才能推到全局。
具体的,对于一个集合 \(S\) 中的元素,要满足以下几点:
-
非自反性:对于任意的 \(x\),要有 \(x<x\) 不成立。
-
非对称性:对于任意的 \(x,y\),若 \(x<y\) 成立,要满足 \(y<x\) 不成立。
-
传递性:对于任意的 \(x,y,z\) 若 \(x<y,y<z\) 均成立,那么要满足 \(x<z\) 也成立。
-
不可比 \(^\dagger\) 的传递性:对于任意的 \(x,y,z\),若 \(x,y\) 不可比,且 \(y,z\) 不可比,那么要满足 \(x,z\) 也不可比。
\(\dagger\):不可比指对于两个元素 \(x,y\),\(x<y\) 与 \(y<x\) 均不成立,在某些特殊比较规则下,这并不代表 \(x=y\) 成立。
只有满足了严格弱序,才能保证排序的时候不等式对所有元素均成立,不会发生冲突,最终求出来的方案才会是最优的,可能有点感性理解,但是如果不满足的话,可能会产生一些元素因为不可比的传递性或者其他性质没满足,导致被错误的移动,最终导致这些元素不满足不等式,继续交换一些仍旧能使答案被优化。
例题
国王游戏:
经典题目,主要写写证明,我认为高精度是不好的,所以六十分就收手。
令前面所有 \(a\) 的乘积为 \(val\),其中某相邻两项为 \(A,B\),二元组分别为 \((a_1,b_1),(a_2,b_2)\)。
则 \(A\) 在前面时,答案 \(ans_1=\max(\frac{val}{b_1},\frac{a_1val}{b_2})\)
\(B\) 在前面时,答案 \(ans_2=\max(\frac{val}{b_2},\frac{a_2val}{b_1})\)
显然有 \(\frac{a_2val}{b_1}>\frac{val}{b_1}\) 和 \(\frac{a_2val}{b_2}>\frac{val}{b_1}\)。
首先给出结论:\(ans_1<ans_2\) 的充要条件为 \(a_1b_1<a_2b_2\)。
可以先自己证一下。
证明
大力分讨。
-
当 \(ans_1=\frac{val}{b_1},ans_2=\frac{val}{b_2}\) 时:
则 \(\frac{a_1val}{b_2}<\frac{val}{b_1}<\frac{val}{b_2}\),与前面条件相悖,此情况不成立。
-
当 \(ans_1=\frac{a_1val}{b_2},ans_2=\frac{val}{b_2}\) 时:
同 1.,显然此情况不成立。
-
当 \(ans_1=\frac{val}{b_1},ans_2=\frac{a_2val}{b_1}\) 时:
有 \(\frac{a_1val}{b_2}<\frac{val}{b_1}<\frac{a_2val}{b_1}\),则变形可得,\(a_1b_1<a_2b_2\)。
-
当 \(ans_1=\frac{a_1val}{b_2},ans_2=\frac{a_2val}{b_1}\) 时:
同 3. 显然 \(a_1b_1<a_2b_2\) 成立。
结论已经明晰,那么想要答案最小化,就将所有二元组按照 \(ab\) 大小从小到大排序即可。
加工生产调度:
依旧是令两个相邻物品分别为 \(A,B\),二元组分别为 \((a_1,b_1),(a_2,b_2)\)。
\(A\) 先加工的答案为 \(ans_1=a_1+b_2+\max(b_1,a_2)\)。
\(B\) 先加工的答案为 \(ans_2=a_2+b_1+\max(b_2,a_1)\)。
可以大力分讨拆 \(\max\)(当然要有惊人的注意力),但是也可以直接把 \(\max\) 带入计算。
若有 \(ans_1<ans_2\),那么有 \(a_1+b_2+\max(b_1,a_2)<a_2+b_1+\max(b_2,a_1)\)。
考虑移项得:\(\max(b_1,a_2)-a_2-b_1<\max(b_2,a_1)-b_2-a_1\)。
观察上式意义,相当于消去左右当中 \(\max\) 的大项,并留下小项的相反数,等价于:
\(-\min(b_1,a_2)<-\min(b_2,a_1)\Rightarrow \min(b_2,a_1)<\min(b_1,a_2)\)
但是上述不等式不满足不可比的传递性,因为 \(\min\) 或 \(\max\) 运算会导致最终作为比较的二元组中的元素不同,最终导致存在 \(x,y\) 不可比且 \(y,z\) 不可比但是 \(x,z\) 却是可比的情况,所以我们需要补充上一个确定的比较规则,最终使严格弱序成立。
我们发现最终答案会与 \(a\) 和 \(b\) 的大小相关,所以可以当上式不可比时,继续比较 \(a\) 或 \(b\) 的大小即可(二者选其一,\(a\) 是小在前,\(b\) 是大在前)。
强烈建议去看参考资料的 \(2\) 与 \(3\)。
反悔贪心
贪心的缺点是什么?
就是容易从局部最优解一下钻进坑里,最终出不来了,无法得到全局最优解。这当然是我们所不希望的。
所以就有了反悔贪心这种方式,在原来贪心的方案基础上一步步修正,从而得到全局最优解。
说到底反悔贪心并没有通式,只是一种思想,具体方法由具体要求确定。
而我们为了维护反悔操作,一般会使用堆这种数据结构,当然,不排除使用其他数据结构的可能性。
例题
[USACO09OPEN] Work Scheduling G:
首先是典中典题。
首先贪心的想就是能做就做,截止时间越早的要越先做,所以先按 \(D_i\) 从小到大排序,接着做一个计时器加一,如果有冲突就考虑反悔,把前面权值比当前更小的替换成当前的权值,朴素小根堆维护即可。
接着是反悔贪心要跨入门所必须的:
[国家集训队] 种树:
我们考虑基础的贪心是怎样的,显然就是每次选合法的最优的,如果选到负数就不选了,但是这样是错的。
Hack 构造是简单的(请自行脑补成环形):\(7,10,7,3\)。
上述贪心选出来的是 \(13\),而正解则是 \(14\)。
考虑反悔策略,我们每次依旧是先选最优的点,但是选完之后加入一个虚点,权值为该点的合法前驱和合法后继的权值和减去该点权值。
这种权值代表了什么呢?实际上就是把该点的权值取消,然后加入两个新点的权值,多次复合的虚点代表含义类似,相当于多加了一个点,除去反悔操作其他都是一致的。
通过这两题可以发现,我们就是通过某种特定的操作来取消之前局部更优但是全局不优的贡献,再加入对于全局更优的贡献,最终达到全局最优状态。
当然,我们的贪心和反悔可以不是一步到位的,可以一步一步来,先从一个全局较优状态再到全局更优状态,最后再从更优到最优。
比如下面这道题,我的解法就是从全局优秀解一步一步最终走到全局最优解。
Cardboard Box:
我们伟大的神 lyyi2003 在考场现切得到了强大的五个堆反悔贪心解法直接由黑降紫,我不明白率先提出全新做法的神怎么能被挤下去,大家快去点赞!
伟大的神提出的伟大解法不应该由我阐述,大家可以自行观看,这里给出鄙人的卑劣的做法。
考虑最基础的贪心,每次选价钱最小的,如果是第一次被选中,那么就同时加入当前位置第二颗所需要代价,即:\(b_i-a_i\),这样你可以得到一个比较优秀的局部解,但是这样连样例二也过不去。
考虑进行修正,我们发现无法通过的原因是有些被加入的位置存在第二颗并未被加入,而这些未被加入的第二颗星可能会比某些一开始更为优秀的已经被加入的位置的第二或第一颗星优秀,我们可以通过堆来维护,弹出那些不优的,加入那些更优的。
但是这样还是不对,虽然已经可以通过样例,但是可以简单地给出 hack 构造,只要构造出某一位置的第一颗星所需代价更优,两颗一起更劣,控制最终要选取的数量,就可以简单的构造出 hack。
我们思考一下这样实际上是什么问题,实际上就是某一个从未被选取的位置两个一起取比某两颗已经被取的星更为优秀,我们只要考虑整体替换两颗星就可以得到最后的最优解,这样已经覆盖所有的状态了。
实现上尤其要注意第二次修正,第二次修正中替换两个星有两种情况,一是在每个位置的最顶上的星(第二颗比第一颗更高)选取两个最劣的出来,二是在存在两颗星都已被选取的位置选一个最劣的出来,两种情况取最劣的出来替换,直到无法替换为之,并且要注意可能会有一颗星同时存在两个堆里,如果一颗星被删除,那么她在这两个堆中都不应该出现。
一开始就写可删堆的话可能会方便许多,但是懒惰删除也能做,要注意封装,不要像我一样写一堆东西但是不封装后面还换堆写。
评测记录。
就写到这里罢,有可能是我退役前的最后一篇学习笔记了。