算法导论16.1-1
题目要求为活动选择设计一个动态规划算法。
1. 问题描述
假定有一个n个活动的集合
S={a1,a2,...,an}
,这些活动使用同一个资源,而这个资源在某个时刻只能供一个活动使用。如果任务
ai
被选中,那么发生在
[si,fi)
期间。如果两个活动
ai
和
aj
不重叠,即
si≥fj
或
sj≥fi
,那么它们是兼容的。我们希望求解问题的一个最大兼容活动集(也就是元素最多的)。
2. 活动选择问题的最优子结构
设
Sij
:在
ai
结束后开始,且在
aj
开始前结束的活动集合。也就是说
Sij
是不包含
ai
和
aj
的。用数学的方法表示就是
s[p]≥f[i]
且
f[p]≤s[j]
,其中
p
为
c[i,j]
:集合
Sij
的最优解的大小。
假定
Aij
为
Sij
的最优解,且
ap
为其中的活动,那么最优解
Aij
必定包含子问题
Sip
和
Spj
的最优解,证明方法为剪切-粘贴法,此处从略。
于是乎,
c[i,j]
可以得到如下递归式
当不知道 Sij 的最优解包含的活动时,就需要比较 Sij 中所有的活动,因此
3. 计算最大兼容活动集的模
为了创建最优解,我们要跟踪 p 。算法只接受
另外,我们还需要一个表 k 来记录每次计算得到的
DYNAMIC-ACTIVITY-SELECTOR(s,f)
n=lenght(s)
s[n+1]=INT
f[n+1]=INT
for i=0 to n
c[i,i+1]=0
k[i,i+1]=-1
for l=1 to n //Sij的元素个数
for i=0 to n-l+1
j=i+l+1
c[i,j]=0
k[i,j]=-1
if f[i]<s[j]
for p=i+1 to j-1
if s[p]>=f[i] and f[p]<=s[j]
Kval=c[i,p]+c[p,j]+1
if Kval>c[i,j]
c[i,j]=Kval
k[i,j]=p
return c and k
构造解的伪代码:
PRINT-SELECTOR(i,j)
if k[i,j]=-1
return
print a k[i,j]
PRINT-SELECTOR(i,k[i,j])
PRINT-SELECTOR(k[i,j],j)
使用C++实现的程序:
#include<iostream>
#include<string>
#include<algorithm>
#define NUM 50
#define INT 65536
using namespace std;
int k[NUM][NUM];
int c[NUM][NUM];
int s[NUM];
int f[NUM];
void DYNAMIC_ACTIVITY_SELECTOR(int n) //n is s's length
{
s[n + 1] = INT;
f[n + 1] = INT;
for (int i = 0; i <= n; i++){
c[i][i + 1] = 0;
k[i][i + 1] = -1;
}
for (int l = 1; l <= n; l++){
for (int i = 0; i <= n - l + 1; i++){
int j = i + l + 1;
c[i][j] = 0;
k[i][j] = -1;
if (f[i] < s[j]){ //is there room between i and j
for (int p = i + 1; p <= j - 1; p++){
if (s[p] >= f[i] && f[p] <= s[j]){
int Kval = c[i][p] + c[p][j] + 1;
if (Kval>c[i][j]){
c[i][j] = Kval;
k[i][j] = p;
}
}
}
}
}
}
}
void PRINT_SELECTOR(int i, int j)
{
if (k[i][j] == -1)
return;
cout << "a" << k[i][j]<<" ";
PRINT_SELECTOR(i, k[i][j]);
PRINT_SELECTOR(k[i][j], j);
}
void main()
{
for (int i = 1; i <= 11; i++)
cin >> s[i];
for (int i = 1; i <= 11; i++)
cin >> f[i];
DYNAMIC_ACTIVITY_SELECTOR(11);
PRINT_SELECTOR(0, 12);
cout << endl;
}
4.运算结果展示