问题的描述
设有n个会议的集合C={1,2,……,n},其中每个会议都要求使用同一个资源(如会议室),而在同一时间内只能有一个会议使用该资源。每个会议i都有要求使用该资源的起始时间bi和结束时间ei,且bi<ei。如果选择了会议i使用会议室,则他在半开区间[bi,ei)内占用该资源。如果[bi,ei)与[bj,ej)不相交,则称会议i与会议j是相容的。会场安排问题的要求在所给的会议集合中选出最大的相容活动自己,也即尽可能地选择更多的会议来使用资源。
算法设计
算法策略:贪心策略
数据结构:数组
- 初始化,并按会议结束时间非减序排序:开始时间存入数组B中,结束时间存入数组E中;按照结束时间的非减序排序E,B需做相应的调整;集合A存储解,如果会议i再集合A中,当且仅当其被选中。
- 根据贪心策略,做第一次贪心选择:令A[1]=1。
- 依次扫描每一个会议,直至所有会议检查完毕:如果会议i的开始时间不小于最后一个选入A中的会议的结束时间,则将会议i加入A中;否则,放弃并继续检查下一个会议与A中会议的相容性。
描述算法(伪代码)
GREEDY-ACTIVITY-SELECTOR (B , E)
SORT-ASC-BY-F(B,E) //对E按非减序排序并同调整E
N=B.length
A={1} //首先选择会议1
K=1 //已被选择会议集合中最晚结束的会议(k)
for m=2 to n
if B[m]>=E[k] //会议m的开始时间不小于会议k的结束时间
then A=A∪{m} //将会议m加入到集合A中
k=m //此时集合A中最晚结束的会议为m
return A
算法的正确性证明
假设最优解不含贪心选择,即最优解A的某个活动a所选择的会场r‘不是结束时间最早的会场r,由于r的结束时间小于r’,a能在r中进行,说明a必定能在r中进行,因此用r代替r‘的结果(A-{r’})∪{r}仍是一个最优解,即最优解包含贪心选择,与假设矛盾。
算法复杂性分析
时间复杂性:O(nlogn)+O(n)->O(nlogn)
空间复杂性:O(logn)+O(1)->O(logn)
算法实现与测试
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int N;
int b[30];
int e[30];
bool a[30];
void GreedSelector();
void swap();
void timesort();
int main()
{
cout<<"输入会议个数:";
cin>>N; //会议个数
cout<<"分别输入每个会议的开始时间和结束时间:"<<endl;
for(int i=1;i<=N;i++){
cin>>b[i]>>e[i];
}
fill(a,a+N,0);
timesort();
GreedSelector();
return 0;
}
void GreedSelector()
{
int i,j;
a[1]=true;
j=1;
i=2;
for(i=2;i<=N;i++){
if(b[i]>=e[j]){
a[i]=true;
j=i;
}else{
a[i]=false;
}
}
cout<<"会议集合为{";
for(int i=1;i<=N;i++){
if(a[i]){
cout<<i<<" ";
}
}
cout<<"}"<<endl;
}
void swap(int a,int b){
int t;
t=a;
a=b;
b=t;
}
void timesort(){
int i,j;
for(j=1;j<N+1;j++){
for(i=1;i<N+1-j;i++){
if(e[i]>e[i+1]){
swap(e[i],e[i+1]);
swap(b[i],b[i+1]);
}
}
}
cout<<"按结束时间排序后的会议排列表:"<<endl;
for(i=1;i<=N;i++){
cout<<i<<" ";
}
cout<<endl;
for(i=1;i<=N;i++){
cout<<b[i]<<" ";
}
cout<<endl;
for(i=1;i<=N;i++){
cout<<e[i]<<" ";
}
cout<<endl;
}