有一个由n个活动组成的集合S = {a1, …, an} 1. 这些活动使用同一个资源,而这个资源在某一时刻只供一个活动使用 2. 每个活动都有一个开始和结束时间si/fi;如果被选中,则任务ai发生在半开时间区间[si, fi) 3. 如果两个活动ai和aj不重叠,则称两个活动兼容
活动选择问题,希望选出一个最大兼容活动集
假设活动已经按结束时间递增排好序
最优子结构 Sij表示在ai结束之后,aj开始之前结束之前的活动集合;Aij表示Sij的一个最大兼容活动集 假设Aij包含ak,
则得到两个子问题,寻找Sik以及Skj的最大兼容活动集 用c[i, j]表示集合Sij的最优解的大小,则可得递归式:c[i, j] = c[i, k] + c[k, j] + 1 从而实际上 c[i, j] = 0 if Sij = 空集 max{c[i, k] + c[k, j] +1} ak属于Sij
活动选择问题的另一个性质,使得无须求解所有子问题就可以选择出一个活动加入到最优解;选择的标准为,选出活动后剩下的资源应能被尽量多的其他任务所用
【选择最早结束的活动】 定理可以证明:任意非空子问题Sk,am是Sk中结束时间最早的活动,则am在Sk的某个最大兼容活动子集中【
假设一个最大兼容活动子集Ak,用am替代Ak中最早结束的活动得到Ak’,依然是最大兼容活动子集】 ——>贪心算法,自顶向下的设计,做出一个选择,然后求解剩下的那个子问题,而不像动态规划自底向上地求解很多子问题,然后再做出选择
假设已经按照活动结束时间排好序
递归贪心算法: RECURSIVE-ACTIVITY-SELECTOR(s, f, k, n)
// s表示活动的开始时间数组;f表示结束时间数组;k表示上一个选中的活动,初始为0【引入a[0],其结束时间为0】;n为问题规模 m = k + 1 while(m<=n && s[m] < f[k])
//活动必须在活动集合中;且其开始时间必须比上一个活动的结束时间晚 m = m + 1 if m<=n return a[m] 并上
RECURSIVE-ACTIVITY-SELECTOR(s, f, m, n)
else
return 空集
迭代贪心算法:(通常“尾递归”的算法都可以很直接的转化为迭代形式) GREEDY-ACTIVITY-SELECTOR(s, f) n = s.length A = {a1} k = 1 for m = 2 to n if(s[m] >= f[k]) A = A 并上 {am} k = m return A