活动安排问题(贪心)
一、问题描述
设有n个活动的集合 E = { 1 , 2 , . . . , n } E=\left \{ 1,2,...,n \right \} E={1,2,...,n},其中每个活动都要求使用同一资源(如演讲会场),而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间 s i s_{i} si和和一个结束时间 f i f_{i} fi,且 s i ≤ f i s_{i} \le f_{i} si≤fi。如果选了活动 i i i,则它在半开时间区间 [ s i , f i ) \left [ s_{i} ,f_{i} \right) [si,fi)内占用资源。若区间 [ s i , f i ) \left [ s_{i} ,f_{i} \right) [si,fi)与区间 [ s j , f j ) \left [ s_{j} ,f_{j} \right) [sj,fj)不相交,则称活动 i i i与活动 j j j是相容的。也就是说,当 s i ≥ f j s_{i}\ge f_{j} si≥fj或 s j ≥ f i s_{j}\ge f_{i} sj≥fi时,活动 i i i与活动 j j j相容。活动安排问题要求在所给的活动集合中选出最大的相容活动子集。
二、问题分析
首先给出活动安排问题的贪心算法geedySelector,各活动的起始时间和结束时间存储于数组 s s s和 f f f中且按活动结束时间的非减序排列。
void GreedySelector(int n,in s[],int f[],bool A[]){
A[1]=true;
int j=1;
for(int i=2;i<=n;++){
if(s[i]>=f[j]){
A[i]=true;
j=i;
}
else A[i]=false;
}
}
贪心法求解活动安排问题的关键是如何选择贪心策略,使得按照一定的顺序选择相容活动并能够安排尽量多的活动。至少有两种看似合理的贪心策略:
(1)最早开始时间,这样可以增大资源的利用率。
(2)最早结束时间,这样可以使下一个活动尽早开始。
由于活动占用资源的时间没有限制,因此,后一种贪心选择更为合理。直观上,按这种策略选择相容活动可以为未安排的活动留下尽可能多的时间,也就是说,这种贪心选择的目的是使剩余时间段极大化,以便安排尽可能多的相容活动。
为了在每一次贪心选择时快速查找具有最早结束时间的相容活动,可以将n个活动按结束时间非减序排列。这样,贪心选择时取当前活动集合中结束时间最早的活动就归结为取当前活动集合中排在最前面的活动。
若被检查的活动 i i i的开始时间 s i s_{i} si小于最近选择的活动 j j j的结束时间 f j f_{j} fj,则不选择活动 i i i,否则选择活动 i i i加入集合 A A A中。
算法greedySelector的效率极高。当输入的活动已按结束时间的非减序排列,算法只需 O ( n ) O(n) O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。如果所给出的活动未按非减序排列,可以用 O ( n l o g 2 n ) O(nlog_{2}{n}) O(nlog2n)的时间重排。
贪心算法并不总能求得问题的整体最优解。但对于活动安排问题,贪心算法greedySelector却总能求得的整体最优解,即它最终所确定的相容活动集合A的规模最大。这个结论可以用数学归纳法证明。