活动选择问题:就是给定一组活动的开始时间和结束时间,然后他们都需要使用到一个资源,这个资源每次只有一个活动可以用,要求求出一个最大的相互兼容的活动子集。
首先定义了一个集合Sij = {ak∈ S :fi ≤ sk <</span> fk ≤ sj} , 其中S就是所有活动的集合,fi是活动ai的完成时间si是活动ai的开始时间。
这道题如果是用DP来解的话,就需要找到最优解的递归方程(其中c[i,j]是集合Sij中的最大兼容活动的个数):
然后根据DP自底向上的解答方法进行求解,过程和矩阵连乘的差不多。
然而,根据下面的定理,我们可以用贪心的策略来解答这道题目:
这样子就将DP的两个子问题变成了只有一个子问题,而且由于将结束时间已升序排序,所以要求出fm也是很简单的。
接下来的工资就变得很简单了,看下面的伪代码和图片即可
- RECURSIVE-ACTIVITY-SELECTOR(s, f, i, j)
- 1 m ← i + 1
- 2 while m < j and sm < fi ▹ Find the first activity in Sij.
- 3 do m ← m + 1
- 4 if m < j
- 5 then return {am} ∪ RECURSIVE-ACTIVITY-SELECTOR(s, f, m, j)
- 6 else return Ø
下面的图片是利用上面的递归算法求解的过程
代码如下:
#include<iostream>
using namespace std;
/*
*start 活动开始时间;finish 活动结束时间(已排序);record 记录贪心算法中所记录的活动顺序
*返回最多活动数
*/
int Greedy_Activity_Selector(int *start,int *finish,int *record,int length)
{
int actmin , i, j;
record[0] = start[0];
j = 0;
actmin = finish[0];
for(i = 1; i < length; i++)
{
if(start[i] > actmin)
{
actmin = finish[i];
record[++j] = start[i];
}
}
return (j + 1);
}
int main()
{
int start[] = {1,3,0,5,3,5,6,8,8,2,12};
int finish[] = {4,5,6,7,8,9,10,11,12,13,14};
int record[11];
int actnum = Greedy_Activity_Selector(start,finish,record,11);
cout<<"The activity num is : "<<actnum<<endl;
cout<<"it contains : ";
for(int i = 0;i < actnum; i++)
cout<<record[i]<<" ";
cout<<endl;
return 0;
}