4.1 活动选择问题
1.问题的理解与描述
假定有n个需要使用同一个这样的资源的活动,每次只能有一个活动使用该资源。每一个活动有一个开始时间 si,一个完成时间 fi,其中0 ≤ si < fi < ∞。如果区间[si , fi)和[sj , fj)不相交,活动ai和aj是相容的(即如果si≥ fj 或 sj ≤ fi,ai和aj相容)。活动选择问题是选取一个由相容活动构成的最大集合。
输入:按完成时间排好序的活动开始时间数组s,完成时间数组 f。
输出:表示一个最大的相容活动组的向量{x1, x2, …, xn},其中
xi={01第i个活动在此最大相容活动组中否则,i1,2,...,n。
2.最优子结构
定义集合
Sij=ak∈S:fi≤sk<fk≤sj ,为表示整个问题,添加两个假想的活动a0和an+1并约定f0 = 0及sn+1 = ∞。于是S = S0 n+1,而i和j的取值范围为0 ≤ i,j ≤ n + 1。显然只要i≥ j,就有Sij =∅。假定Sij的一个解包含活动ak,每一个都构成Sij中的活动的子集。设Aij是Sij的一个最优解, ak∈Aij 。利用 ak 产生两个子问题: Sik和Skj ,则Aij中Sik的解Aik和Skj的解Akj必对于: Sik和Skj也是最优的。
3.贪婪选择性
定理4-1
考虑任一非空子问题Sij,并设am为Sij中最早完成的一个活动:
fm=minfk:ak∈Sij 。
则
(1)活动 am 包含在 Sij 的一个最大相容活动子集合中。
(2)子问题 Sim 是空的,所以选择 am 将使得 Smj 成为仅有的非空子问题。
4.算法的伪代码描述
RECURSIVE-ACTIVITY-SELECTOR (s, f, i, j)
1 m ← i + 1
2 while m < j 且 sm < fi 求Sij中的第一个活动
3 do xm←0
4 m ← m + 1
5 if m < j
6 then xm←1
7 RECURSIVE-ACTIVITY-SELECTOR (s, f, m, j)
算法运行时间:Θ(n)
5 C++代码实现
#ifndef _GREEDYACTIVITYSELECT_H
#define _GREEDYACTIVITYSELECT_H
int* RecusiveActivitySelector(int* s, int* f,int i,int j);
#endif
#include "GreedyActivitySelect.h"
int* RecusiveActivitySelector(int* s, int* f,int i,int j)
{
static int * x=new int[j];
int m=i+1;
while(m<j&&s[m]<f[i])//求s[ij]中的第一个活动
{
x[m]=0;
m++;
}
if(m<j)
{
x[m]=1;
RecusiveActivitySelector(s,f,m,j);
}
return x;
}
#include "GreedyActivitySelect.h"
#include <limits>
#include <iostream>
using namespace std;
int main(int argc,char** argv)
{
int* x;
int s[]={0,1,3,0,5,3,5,6,8,8,2,12,numeric_limits<int>::max()},
f[]={0,4,5,6,7,8,9,10,11,12,13,14,numeric_limits<int>::max()};
x=RecusiveActivitySelector(s,f,0,12);
for(int i=1;i<12;i++)
cout<<x[i]<<" ";
cout<<endl;
delete[]x;
system("pause");
return (EXIT_SUCCESS);
}
运行结果:1 0 0 1 0 0 0 1 0 0 1