16.1-2
- 问题描述
不再一直选择最早结束的活动,而是选择最晚开始的活动,前提仍然是与之前选出的所有活动均兼容。描述利用该方法设计的贪心算法,并证明算法会产生最优解。 贪心选择
令 Sk={ai∈S:fi≤sk} 即 ak 开始前结束的任务集合。当我们做出贪心选择 al ,那么剩下的 Sl 是唯一需要求解的子问题。我们先证明“如果 am 是 Sk 中最迟开始的活动,那么 am 在 Sk 的某个最大兼容活动子集中。”
证明:
令 Ak 是 Sk 的最大兼容活动子集, aj 为 Ak 中最迟开始的活动。如果 aj=am ,那么 am 在 Sk 的某个最大兼容活动子集 Ak 中;如果 aj≠am ,令集合 A′k=(Ak−{aj})⋃{am} 。因为 Ak 中的活动是不重叠的,而 aj 为 Ak 中最迟开始的活动, sm≥sj 即 am 开始的时间比 aj 开始的时间还迟,所以 A′k 也是不相交的,因此 |A′k|=|Am| ,即 A′k 也是 Sk 的一个最大兼容活动子集,因此 am 是在 Sk 的某个最大兼容活动子集 A′k 中的。递归贪心算法
假设输入是按照 si 递减顺序排列的。
RECURSIVE-ACTIVITY-SELECTOR(s,f,k,n)
m=k+1
while m<=n and f[m]>s[k]
s[0]=INT
m=m+1
if m<=n
return {am}并RECURSIVE-ACTIVITY-SELECTOR(s,f,m,n)
else
return
4.C++实现
输入样例
i 1 2 3 4 5 6 7 8 9 10 11 si 12 8 8 6 5 5 3 3 2 1 0 fi 16 12 11 10 9 7 5 9 14 4 6
C++程序:
#include<iostream>
#include<string>
#include<algorithm>
#define NUM 50
#define INT 65536
using namespace std;
int s[NUM];
int f[NUM];
void RECURSIVE_ACTIVITY_SELECTOR(int k,int n)
{
s[0] = INT;
int m = k + 1;
while (m <= n &&f[m] > s[k])
m = m + 1;
if (m <= n){
cout << "a" << m << " ";
RECURSIVE_ACTIVITY_SELECTOR(m , n);
}
return;
}
void main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> s[i];
for (int i = 1; i <= n; i++)
cin >> f[i];
RECURSIVE_ACTIVITY_SELECTOR(0, n);
cout << endl;
}
输出结果: