除了最优子结构性质之外,与动态规划不同的是,使用贪心算法解决的问题必须符合贪心选择性质:
通过作出局部最优(贪心)选择来构造全局最优解。
在解决符合贪心选择性质的问题时,与动态规划算方法相比,贪心算法更简单、更高效。
活动安排问题
假定有一个n个活动的集合S={a1,a2,...,an},这些活动使用同一个资源(例如一个教室),而这个资源在某个时刻只能供一个活动使用。
每个活动ai都有一个开始时间si和一个结束时间fi。我们希望选出一个最大兼容活动集(其中两两活动都不重叠)。例如,考虑下面的活动集合S:
已按结束时间的单调递增顺序排序:
我们可以使用贪心算法解决这个问题(可以有其他的贪心选择方案):
每次我们从可以选择的活动集中选择最早结束的活动,并把它加入最优解。可以选择的活动集是S中开始时间大于fi(i是上次选择的活动)的活动。
因为活动已按结束时间的单调递增顺序排序,因此第一次选择a1加入最优解。然后每次执行贪心选择,得到最优解。
可以简单的证明一下:在满足相容条件下,使结束时间靠前的活动得到资源,这样为后续留下更多的时间,以使更多的活动得到安排。
伪代码
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
其中A代表最大兼容的活动集,k记录了最近加入集合A的活动的下标。
因为活动已按结束时间的单调递增顺序排序,所以只需要每次找出可以选择的活动集中的第一个活动加入A中。
代码的实现与测试
1 #define N 11 2 #include <iostream> 3 4 using namespace std; 5 6 void greedy_activity_selector(int s[],int f[],int a[]) 7 { 8 int i=0; 9 a[i]=1; 10 int k=1; 11 for(int m=2;m<=N;++m) 12 { 13 if(s[m]>=f[k]) 14 { 15 a[++i]=m; 16 k=m; 17 } 18 } 19 } 20 21 22 int main() 23 { 24 int s[]={0,1,3,0,5,3,5,6,8,8,2,12}; 25 int f[]={0,4,5,6,7,9,9,10,11,12,14,16}; 26 int a[N]={0}; 27 greedy_activity_selector(s,f,a); 28 int i=0; 29 //输出最大兼容活动集的活动 30 while(a[i]) 31 cout<<a[i++]<<" "; 32 cout<<endl; 33 return 0; 34 }