活动选择(贪心算法)

参考:【算法导论】贪心算法之活动选择问题

一、贪心算法

贪心算法(Greedy Algorithm)在每一步都做出当时看起来最佳的选择,寄希望这样的选择能导致全局最优解。
这种算法并不能保证得到最优解,但对很多问题确实可以求得最优解。

二、活动选择问题

问题描述

假定有一个n个活动的集合S={a1,a2,a3,...,an},这些活动使用同一个资源,而这个资源在某个时刻只能给一个活动使用。每个活动都有一个开始时间si和一个结束时间fi,其中0<=si 如果活动ai被选中,则此活动发生在半开区间[si,fi)中。
若两个活动ai和aj的时间区间不重叠,则称这两个活动是兼容

在活动选择问题中,我们希望选出一个最大兼容活动集。
假定活动已经按照结束时间递增顺序排好序
f1<=f2<=f3<=...<=fn

考虑如下例子:

可以看到,{a3,a9,a11}是由相互兼容的活动组成。但它不是一个最大集,{a1,a4,a8,a11}更大,是一个最大集。(最大集不唯一)

三、最优子结构

假设:Sij表示在ai结束之后,在aj开始之前的活动的集合。Aij表示Sij的一个最大相互兼容的活动子集。
那么只要Sij非空,则Aij至少会包含一个活动,假设为ak。那么可以将Aij分解为:Aij = Aik+ak+Akj。
假设Cij为Aij的大小,那么有Cij=cik+ckj+1。
于是,我们可以利用动态规划得到这个问题的递归解

我们当然可以利用动态规划自底向上地求解这个问题,但是我们可以利用贪心算法更快地求解问题答案。

四、贪心选择

我们选择活动结束时间最早的那个活动,这样能够给其他活动尽可能的腾出多余的时间,而后每一步都在剩下的活动中选取最早的活动,这样就可以获得一个最优解。

为什么贪心选择——最早结束的活动ai——总是最优解的一部分呢?

证明

假设Aij是Sij的某个最大兼容活动集,假设Aij中,最早结束的活动是an。(an是最优解中最早结束的,不一定是原先活动中最早结束的)我们要证明我们选择的a1(原先活动集中最早结束的)也在最优解中。
分两种情况:

①如果an=a1,则得证

②如果an不等于a1,则an的结束时间一定会晚于a1的结束时间,我们用a1去替换Aij中的an,于是得到A',由于a1比an结束的早,而Aij中的其他活动都比an的结束时间开始 的要晚,所以A'中的其他活动 都与a1不相交,所以A'中的所有活动是兼容的,所以A`也是Sij的一个最大兼容活动集。
(简单说,就是用a1替换an,得到另一个解A',由于a1最早结束,当然与其他活动不相交,于是A'也兼容且个数和A一样,所以A'也是最优解)

于是证明了命题。

实现

通过以上分析,我们可以反复地选择最先结束的活动,保留于此活动兼容的活动,重复执行,直到不再有剩余活动。
贪心算法通常是自顶向下地设计:做出一个选择,然后求解剩下的那个子问题

伪代码

递归形式

为了方便初始化,我们添加一个虚拟活动a0,其结束时间为f0=0

迭代形式

时间复杂度

由于我们之前就已经将活动按结束时间排好序,每一次找元素都只对元素访问一次,所以贪心算法的时间复杂度是大theta(n)

你可能感兴趣的:(活动选择(贪心算法))