【贪心法】--活动安排问题
1、活动安排
设有n个活动的集合 E = {1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。
每个活动 i 都有一个要求使用该资源的起始时间 si 和一个结束时间 fi,且 si < fi。如果选择了活动i,则它在半开时间区间 [si ,fi ) 内占用资源。若区间 [si , fi )与区间 [sj, fj ) 不相交,则称活动i与活动j是相容的。当 si ≥ fj 或 sj ≥ fi 时,活动 i 与活动 j 相容。
活动安排问题就是在所给的活动集合中选出最大的相容活动子集合。
例如:
2、算法分析
【时间复杂度&&优化】
该算法只用一层循环,当结束时间f[i]数组按照非减序排列时,该算法的时间复杂度是O(n);
若结束时间f[i]数组未排好序,则至少用O(nlogn)的时间进行重排;
【算法精髓】
每次在循环(有且仅有1轮循环)中,用j进行更新,来标记哪个活动是可安排的,即b数组,从而避免了两层循环套循环的复杂度;
b数组中标记为1的活动,互相之间是可相容的。
【最优量度标准】
即如何判断两个活动之间是可相容的,s[i]>=f[j] 若下一活动的开始时间晚于之前活动的结束时间,则这两个活动是相容的
【源代码】#include <iostream>
using namespace std;
const int N = 11;//活动的数量
void GreedySelector(int n, int s[], int f[], bool A[])
{
A[1]=true;//默认将第一个活动先安排
int j=1;//记录最近一次加入A中的活动
for (int i=2;i<=n;i++)//依次检查活动i是否与当前已选择的活动相容
{
if (s[i]>=f[j])//下一活动的开始时间晚于之前活动的结束时间
{
A[i]=true;//标记该活动是可安排的
j=i;
}
else
{
A[i]=false;//标记该活动是不可安排的
}
}
}
int main()
{
int s[] = {0,1,3,0,5,3,5,6,8,8,2,12}; //下标从1开始,存储活动开始时间
int f[] = {0,4,5,6,7,8,9,10,11,12,13,14}; //下标从1开始,存储活动结束时间
bool b[N+1];//存储被安排的活动编号
cout<<"各活动的开始时间,结束时间分别为:"<<endl;
for(int i=1;i<=N;i++)
{
cout<<"["<<i<<"]:"<<"("<<s[i]<<","<<f[i]<<")"<<endl;
}
GreedySelector(N,s,f,b);
cout<<"最大相容活动子集为:"<<endl;
for(int i=1;i<=N;i++)
{
if(b[i]){
cout<<"["<<i<<"]:"<<"("<<s[i]<<","<<f[i]<<")"<<endl;
}
}
return 0;
}