基于贪心策略的活动选择问题

关于活动选择的问题:
1:问题描述
n个活动都有自己的开始和结束时间,但是每个活动都是占用同一个教室,问怎么安排活动能够使一天
内安排的活动总数最多。


2:算法解决思想
活动总共是从1到n总共n个,假设Sij表示的是活动ai结束之后,aj开始之前的全部活动,现在我们来求
Sij的一个最大兼容活动子集。假设Aij是满足这样条件的一个最大活动子集,并且它包含了活动ak。那么可
以将Aij分割为3部分,既Aij = Aik + ak + Akj。则Sij的最大兼容活动个数sum = |Aik| + 1 +|Akj|。
如果Aik不是活动Sik的一个最佳子集,那么必然存在一个Sik' > Sik,那么就会有sum' = |Aik’| + 1 + |Akj| > sum,
这根sum是Sij的最大兼容活动个数相违背,所以Aik必然是活动集Sik的一个最大兼容活动集,也就是说子问题Aik是子问题
的最优解。同理Akj也必然是子问题的最优解。这就满足了最优解的问题的子问题也是最优解的条件,预示着我们可以
使用动态规划来解决这个问题。


3:关键点的证明
虽然使用动态规划可以解决问题,但是动态规划的时间复杂度要高些,并且动态规划要解决的子问题比较多些。
贪心选择就是从直觉上选择当前应该是最佳的解决方案,那么两个子问题中因为有了贪心选择就只剩下一个待解决的
子问题了。在活动选择这里,我们做的选择就是结束最早的活动。那怎么证明最早结束的就一定存在在最优解中呢?
在这里有个定理:在非空子问题Sk中,如果am是Sk中结束最早的活动,那么am在Sk的某个最大兼容活动集中。
证明是这样的:设Ak是Sk的一个最大兼容活动集,且aj是Ak中最早结束的活动,若aj == am,则证明了定理,若
aj≠am,令Ak' = (Ak - aj)∪am,因为f(am) < f(aj),所以Ak’中的活动必然是不相交的,那么就可以得出Ak'也是Sk
的一个最大兼容活动子集。从而证明了定理。


4 代码实现
由于python代码接近于伪代码,所以这里采用python来实现算法
<1> 递归
# k is the activity which has finished and n has't finished
def greedyActivity(s, f, k, n):
    m = k + 1
    while (m <=n and s[m] < f[k]):
        m += 1
    if(m <= n):
        x = [m];
        x += greedyActivity(s, f, m, n)
        return x
    else:
        return []


#in order let the first activity in the result ,we must make up 
#an activity as the one  activity finished before the first
s = [-2,1,3,0,5,3,5,6,8,8,2,12]
f = [-1,4,5,6,7,8,9,9,10,11,12,14,16]
k = 0
n = 11
z = greedyActivity(s,f,k,n)
print(z)  

<2> 非递归 
#make a for loop and every time findintg the earlist finished activity
# then change the k value
def greedyActivityAdvance(s,f,k,n):
    x = [s[1]]
    k = 1
    for m in range(2,n+1) :
        if( s[m] >= f[k]):
            x.append(m)
            k = m
    return x
s = [-2,1,3,0,5,3,5,6,8,8,2,12]
f = [-1,4,5,6,7,8,9,9,10,11,12,14,16]
k = 0
n = 11
zz = greedyActivityAdvance(s,f,k,n)
print(zz)


5 参考文献

算法导论




你可能感兴趣的:(算法)