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;
}
输出结果: