一、题目
在算法导论第三版的第16章贪心算法的思考题16.1-4中,作者提出了这样一个问题:

二、初步分析与具体案例
看到这个题目,第一时间的想法是用教程中的贪心算法进行循环,但这不是最佳的算法。不仅时间复杂度为O(),且并不能得到最优解。比如对于如下活动:
![]()
会得出需要3个教室,活动安排为:
Room1: [1,4), [6,7)
Room2: [2,5)
Room3: [4,8)
而最优解只需2个教室:
Room1: [1,4), [4,8)
Room2: [2,5),[6,7)
具体解释如下,我就不翻译了:

三、正确的解法
那么,正确的解法是什么呢?在网上搜了很多博客,写的不是很明白,有的代码逻辑也不对,因此给出笔者参考各种文章及教程答案后自己的实现。
核心算法一句话概括如下:
对活动按开始时间升序排序,依次迭代;对已安排活动的教室列表按照可用时间创建一个小顶堆,每次有新活动时,判断堆顶的教室是否可用,可用则用,否则开辟一个新教室安排活动,并将该教室加入小顶堆。(也可用优先队列、有序数组实现小顶堆同样的功能)。
第一步:对活动排序
先对所有的活动按照开始时间升序排序。注意,这里和书上教程例子中的按结束时间排序就不同了。这里要用到排序,快排吧,时间成本 .
因为按照正常的思维,我们安排一个活动时,也是看这个活动什么时间开始。结合着下面看。
第二步:安排合适的教室
这一步很关键,并且有几种情况,还要引入新的数据结构小顶堆。
Case 1:安排第一个活动,也即现在所有的教室都是可用的。这时随便选取一个教室即可,然后把这个教室的可用时间更新为该活动的结束时间。
Case 2:安排第 m 个活动,且原来已经有一些教室被安排了活动,假设这些教室组成了列表busy_list。由于题目让求的是最小的教室数量,那么要充分利用已经被安排过活动的教室。因此,这一步的关键在于,将所有被安排过活动的教室的可用时间做升序排序。排在首位的是最早释放的,我们假设该教室的编号为R1。然后和活动m的开始时间相比:
如果m的开始时间大于等于R1的可用时间(也即此时R1的上个活动已结束),则将m安排进R1的活动列表,同时更新R1的可用时间,并重新对 busy_list 按照可用时间做升序排序(这样保证每次总是选到最早释放的教室,让教室空置时间尽量的少)。
如果m的开始时间小于R1的可用时间,由于 busy_list 是按升序排序的,那么其他活动教室当前更不可用。此时,开辟一个新的教室,并将m安排进

本文介绍了如何利用贪心策略和小顶堆优化活动安排,确保最少教室使用,以Dart为例提供了详细代码实现。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



