贪心算法

作者:刘楚杰,时间:2022年11月8日
未经本人允许禁止转载

什么是贪心算法

贪心算法的正确性

贪心算法的核心思想

贪心算法的技巧

贪心算法的证明方法

贪心算法的常见题型及讲解

最优装载问题

nn个物体,第ii个物体重量为wiw_i,选择尽量多的物体,使得总重量不超过CC

思路:

想要装的越多,当然是一个物体越轻越好,在同样的情况下,装重的肯定没有装轻的划算,因此我们的贪心策略是先装轻的,将这些数据从小到大排序后处理即可

部分背包问题

nn个物体,第ii个物体的重量为wiw_i,价值为viv_i,在总重量不超过CC的情况下让总价值尽量高。每一个物体可以只取走一部分,价值和重量按比例计算。

思路:

这道题问的是性价比,而不是单纯的价值或者重量,因此我们需要处理的量为viwi\frac{v_i}{w_i},因为每一个物体可以仅取走一部分,因此我们保证背包可以装满,我们只需要选取当前性价比最高的装进去即可,选用结构体,对每个物体的性价比从大到小排序后处理即可

乘船问题

nn个人,第ii个人重量为wiw_i。每艘船的载重量均为CC,最多可承两个人。求用最少的船装载所有人的方案。

思路:

将数据从小到大排序,最重的与最轻的在一组,如果当前最重的和当前最轻的不能在一组,则让最重的单独一组,不再考虑这个最重的。

证明:

方法一:

取当前最大的一个,如果其与当前最小的一个无法乘坐同一艘船,那么最大的这个只能够自己乘一艘船,如果他能与别人共一艘船,我们很容易想到:让他与尽可能大的那个人共一艘船,使得他们的重量在不超过船的承重下尽可能的大,但其实这样做是没有意义的,并且会更加浪费时间,我们只需要让其跟重量最小的那个人共一艘船即可,我们可以证明:

方法二:

假设最轻的人和最重的人配对的贪心策略不是最优的,最优方案中的ii是什么样的呢?

情况1:ii不和任何一个人乘同一艘船,那么可以把jj拉过来和他一起乘,总船数不会增加(且可能会减少),并且符合贪心策略

情况2:ii和另外一个人kk同乘一艘船,其中kkjj轻。把jjkk交换后,jj原来所在的船仍然不会超重(因为kkjj轻),因此iijj同乘一艘船,所得到的的新解不会更差,符合贪心策略

选择不相交区间问题

给定nn个开区间(ai,bi)(a_i, b_i),选择尽量多个区间,使得这些区间两两没有公共点。

思路:

对这些区间的右端点从小到大排序,然后按照右端点从小到大的顺序从左边往右边看:如果当前这个区间左端点比前一个选定区间(一定是选定好的区间,而不是前一个区间)的右端点大,则选定这个区间,答案加一,否则,不选定这个区间。

区间选点问题

给定nn个闭区间[ai,bi][a_i, b_i],在数轴上选尽量少的点,使得每个区间内都至少有一个点(不同区间内点可以是同一个)。

思路:

对这些区间的右端点从小到大排序,然后按照右端点从小到大的顺序从左边往右边看:如果当前这个区间左端点比最新选定的点小,则代表这个区间内已经有点了,无需再处理;如果当前这个区间左端点比最新选定的点大,则代表这个区间内还没有选定点,我们选择这个区间的右端点作为新的点。

区间覆盖问题

nn个闭区间[ai,bi][a_i, b_i],选择尽量少的区间覆盖一条指定的线段区间[s,t][s, t]

思路:

首先将左端点小于ss的都将其左端点更改为ss,右端点大于tt的都将其右端点更改为tt,然后对这些区间进行一些处理:左端点相同的不同区间仅留下右端点最大的那个,然后对这些区间的左端点从小到大排序,考虑当前区间的条件是当前区间的左端点在上一个选定区间右端点的左边,这样我们可能会考虑很多个区间,在这些区间中选定右端点最大的那个

流水作业调度问题

nn个作业要在两台机器M1M_1M2M_2组成的流水线上完成加工。每个作业ii都必须先花时间aia_iM1M_1上加工,然后花时间bib_iM2M_2上加工。
确定nn个作业的加工顺序,使得从作业1在机器M1M_1上加工开始到作业nn在机器M2M_2上加工完为止所用的总时间最短。

思路:

我们原先说,贪心算法的核心就体现在“最”字上,而这道题很明显,它的“最”体现为:浪费的时间最少,因为所有的作业都可以等上一个作业完成后立马在M1M_1机器上开始加工,因此在M1M_1上加工并不会浪费任何时间,时间的浪费就出现在了M2M_2机器上

t1t1t2\leq t2时,假设有两个作业AABB,满足:

如果先进行作业AA后进行作业BB,则总时间为T1=A.t1+B.t2+max(B.t1,A.t2)T_1 = A.t1 + B.t2 + max(B.t1, A.t2)
如果先进行作业BB后进行作业AA,则总时间为T2=B.t1+A.t2+max(A.t1,B.t2)T_2 = B.t1 + A.t2 + max(A.t1, B.t2)

则:T1T2=A.t1B.t1+B.t2A.t2+max(B.t1,A.t2)max(A.t1,B.t2)T_1 - T_2 = A.t1 - B.t1 + B.t2 - A.t2 + max(B.t1, A.t2) - max(A.t1, B.t2)

综上:当t1t1均小于等于t2t2时,优先t1t1更小的在只有两个作业时最优

推广到多个作业:

依次类推,可将所有相邻的作业都进行换序,最终的最优解为t1t1从小到大,因此:

t1t1均小于等于t2t2时,优先t1t1更小的结果最优

假设有两个作业AABB,其中AA作业t1t2t1 \geq t2BB作业t1>t2t1 > t2

如果先进行作业AA后进行作业BB,则总时间为T1=A.t1+B.t2+max(B.t1,A.t2)T_1 = A.t1 + B.t2 + max(B.t1, A.t2)
如果先进行作业BB后进行作业AA,则总时间为T2=B.t1+A.t2+max(A.t1,B.t2)T_2 = B.t1 + A.t2 + max(A.t1, B.t2)

则:T1T2=A.t1B.t1+B.t2A.t2+max(B.t1,A.t2)max(A.t1,B.t2)T_1 - T_2 = A.t1 - B.t1 + B.t2 - A.t2 + max(B.t1, A.t2) - max(A.t1, B.t2):分类讨论可得T1T20T_1 - T_2 \leq 0

综上:优先t1t1t2t2小的在两个作业时更优

这个结论很容易推广到多个作业:优先t1t1t2t2小的更优

假设有两个作业AABB,均满足t1>t2t1 > t2

如果先进行作业AA后进行作业BB,则总时间为T1=A.t1+B.t2+max(B.t1,A.t2)T_1 = A.t1 + B.t2 + max(B.t1, A.t2)
如果先进行作业BB后进行作业AA,则总时间为T2=B.t1+A.t2+max(A.t1,B.t2)T_2 = B.t1 + A.t2 + max(A.t1, B.t2)

则:T1T2=A.t1B.t1+B.t2A.t2+max(B.t1,A.t2)max(A.t1,B.t2)T_1 - T_2 = A.t1 - B.t1 + B.t2 - A.t2 + max(B.t1, A.t2) - max(A.t1, B.t2):分类讨论可得T2T10T_2 - T_1 \geq 0

综上:t1t1均大于t2t2时,优先t2t2大的效果更优(很容易由两个作业推广到多个作业)

因此,本题的贪心策略为:优先t1t2t1 \leq t2的,然后为t1>t2t1 > t2的。在t1t2t1 \leq t2中,优先t1t1小的;在t1>t2t1 > t2中,优先t2t2大的

带期限和罚款的单位时间任务调度

nn个任务,每个任务都需要1个时间单位执行,任务ii的截止时间di(1din)d_i(1 \leq d_i \leq n)表示要求任务ii在时间did_i结束时必须完成,误时惩罚wiw_i表示若任务ii未在时间did_i结束之前完成,将导致wiw_i的罚款。
确定所有任务的执行顺序,使得罚款最少。

思路:

先对wiw_i从大到小排序,然后按照排好的顺序一次对任务进行安排。安排的规则为:使处理任务ii的时间既在did_i之内,又尽量靠后;如果did_i之内的时间都已经排满,就放弃处理此项任务。

证明:

假设按照上述算法得到的解不是最优的,那么必然存在某个任务jj应当安排到处理的过程中但却没有安排。假设我们要将该任务安排进去,由于在时间djd_j内都已经排满,就必然需要将一个已安排的任务kk与之替换,而wkwjw_k \geq w_j。这样,替换显然会增加罚款的数额。因此,除上述安排方法以外的安排方法都不会使罚款的数额减少,可知用上述算法得到的结果是最优的。

[NOIP2012]国王游戏

恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这n位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手 上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

思路:

仅考虑两个大臣A与B时,假设大臣A的左手的值为A.left,右手的值为B.left;大臣B的左手的值为B.left,右手的值为B.right。国王左手的值为king.left,国王右手的值为king.right

对于大臣A与大臣B,有两种可能的情况:

因此我们只需要比较四个数的大小:

king.left÷A.rightking.left \div A.rightking.left×A.left÷B.rightking.left \times A.left \div B.rightking.left÷B.rightking.left \div B.rightking.left×B.left÷A.rightking.left \times B.left \div A.right

全部除去king.leftking.left,即我们需要比较:

1A.right,1B.right,B.leftA.right,A.leftB.right\frac{1}{A.right}, \frac{1}{B.right}, \frac{B.left}{A.right}, \frac{A.left}{B.right}

很容易知道:

1A.rightB.leftA.right,1B.rightA.leftB.right\frac{1}{A.right} \leq \frac{B.left}{A.right}, \frac{1}{B.right} \leq \frac{A.left}{B.right}

因此我们只需要比较A.leftB.right\frac{A.left}{B.right}B.leftA.right\frac{B.left}{A.right}即可,比较A.leftB.right\frac{A.left}{B.right}B.leftA.right\frac{B.left}{A.right},也就是比较A.left×A.rightA.left \times A.rightB.left×B.rightB.left \times B.right,若A.left×A.right>B.left×B.rightA.left \times A.right > B.left \times B.right,则我们将A放到B的后面能使得最大的最小。

推广到多个大臣时,假设大臣都已经按位置站好,现在我们任取两个大臣A和B,A在B的前面,如果我们交换A与B:

如果我们不想考虑在A与B之间的大臣,我们可以选择相邻的大臣A与B,这样,交换A与B的顺序,就只会对A与B获得的金币数量造成影响。

对于相邻的A与B,我们根据前面的推断,很容易得知,当A.left×A.rightB.left×B.rightA.left \times A.right \leq B.left \times B.right时,A放在B前面,能够使得A与B获得金币数量最多的的那个获得的金币数量最少

像这样,我们每次都取相邻的两个,按照A.left×A.rightB.left×B.rightA.left \times A.right \leq B.left \times B.right将A放在B的前面,最终我们得到的所有大臣a1,a2,a3,......,an1,ana_1, a_2, a_3, ......, a_{n-1}, a_n的情况应该满足:

a1.left×a1.righta2.left×a2.righta3.left×a3.right...an1.left×an1.rightan.left×an.righta_1.left \times a_1.right \leq a_2.left \times a_2.right \leq a_3.left \times a_3.right \leq ...\leq a_{n-1}.left \times a_{n-1}.right \leq a_n.left \times a_n.right

因此,局部最优满足全局最优,这道题的贪心策略为:将大臣的左手的值乘上右手的值从小到大排序,大臣按照这个顺序站,能实现获得金币最多的那个大臣获得的金币数量最少。

二进制上的贪心问题

如果是对二进制上每一位都有决定权,想要使得最终处理结果最大,那么我们的贪心决策是二进制同位上的1越多越好

例题:

  1. 毒瘤xor
  2. 兔子的区间密码
  3. 起床困难综合症

田忌赛马问题-改编

齐王和田忌每人各有nn匹马,并且保证齐王的每一匹马和田忌的每一匹马的速度均不相同(没有速度相同的马),已知齐王会从最好的马开始出,将他的马由速度从大到小参与各场比赛(每一匹马只能参加一场比赛),现在田忌已知齐王所有马的速度和自己所有马的速度,请问田忌该如何决策才能使自己赢的次数最多

思路:

我们通常会这么想:如果不能赢齐王当前的这匹马,那么我们就选取最差的那匹马去和齐王比,如果能够赢齐王的这匹马,那么我们尽量去选择能赢齐王的所有马中速度最慢的那匹马去和齐王比,但其实这没有必要,反而会增加程序的时间复杂度。

当我们能够赢齐王当前的这匹马时,我们可以直接选用当前最大的那匹马去和齐王比,证明:

最终的贪心策略为:

贪心算法题单

大连理工大学(盘锦)ICPC集训队Online Judge 题单