假设在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排。(这个问题实际上是著名的图着色问题。若将每个活动作为图的一个顶点,不相容活动间用边相连。使相邻顶点有着不同颜色的最小着色数,相当于要找的最少会场数。)
对于给定的 n n n个待安排活动,计算使用最少会场的时间。
由文件input.txt提供输入数据。第1行中有一个整数 n n n,表示有 n n n个待安排的活动。接下来的 n n n行中,每行有2个正整数,分别表示 n n n个待安排的活动的开始时间和结束时间。时间以0点开始的分钟计。
将计算的最少会场数输出到文件output.txt。
会场安排问题可以用贪心算法求解,采用结束时间最早的会场作为贪心选择
算法具体描述:
1)用数组s和f分别存储各活动的开始时间和结束时间,将s按非递减排序,该次序为各活动选择会场的次序;将f按非递减排序, 由于会场的结束时间由活动的结束时间决定,排序后的数组也是会场的结束时间点。
2)先为最早开始的活动开辟一个会场,此时会场的最早结束时间为该活动的结束时间,然后遍历剩下的活动,对于每个活动,判断当前最早结束的会场内是否仍有活动(即会场的最早结束时间大于该活动的开始时间),如果有,开辟一个新会场;如果没有,说明当前最早结束的会场能容纳当前的活动,更新会场的结束时间点,保证最早结束的会场最先开始下一个活动。
算法正确性证明:
1)贪心选择性质的证明
优解包含贪心选择的证明可用反证法,证明如下:
假设最优解不含贪心选择,即最优解 A A A的某个活动 a a a所选择的会场 r ’ r’ r’不是结束时间最早的会场 r r r,由于 r r r的结束时间小于 r ’ r’ r’, a a a能在 r r r中进行,说明 a a a必定能在 r r r中进行,因此用 r r r代替 r ’ r’ r’的结果 ( A − { r ′ } ) ∪ { r } (A-\{r'\})\cup\{r\} (A−{r′})∪{r} 仍是一个最优解,即最优解包含贪心选择,与假设矛盾。
2)最优子结构性质的证明
若 A A A是原问题 E E E的最优解,会场 r 1 r_1 r1安排的活动集合为 e e e,则 A ′ = A − { r 1 } A'=A-\{r_1\} A′=A−{r1}是子问题 E ′ = E − e E'=E-e E′=E−e的最优解,该命题的证明可用反证法,证明如下:
假设 B ′ B' B′是子问题 E ′ E' E′的更优解, B ′ B' B′比 A ′ A' A′所需的会场数更少,由于 E ′ E' E′与 e e e不相容,新开辟一个会场 r r r容纳活动集合 e e e对 B ′ B' B′无影响,那么 B ′ ∪ { r } B'\cup\{r\} B′∪{r}是原问题的更优解,与假设矛盾。
综上,会场安排问题可用贪心算法求解,且将结束时间最早的会场为贪心选择能得到最优解。
设有4个活动,每个活动的开始和结束时间分别为{1, 6}, {4, 8}, {9, 10}, {7, 18}
总共需要2个会场
#include
#include
#include
#include
using namespace std;
int main()
{
int n; //活动个数
int s[100]; //各活动开始时间
int f[100]; //各活动结束时间
int i, j; //i,j分别指向开始时间和结束时间
int ans; //结果
//文件输入
ifstream ifs;
ifs.open("G:\\algorithm\\data\\4_1_input.txt", ios::in);
ifs>>n;
for (int i = 1; i <= n; ++i)
{
ifs>>s[i]>>f[i];
}
ifs.close();
//排序
sort(s + 1, s + n + 1); //将各活动的开始时间按非递减排序,排序后的数组为各活动选择会场的次序
sort(f + 1, f + n + 1); //将各活动的结束时间按非递减排序,由于会场的结束时间由活动的结束时间决定,排序后的数组也是会场的结束时间点
//为最早开始的活动开辟一个会场,此时会场的最早结束时间为该活动的结束时间
j = 1;
ans = 1;
//遍历剩下的活动
for (int i = 2; i <= n; ++i)
{
//判断当前最早结束的会场内是否仍有活动
//如果有,开辟一个新会场
if (f[j] > s[i])
{
ans++;
}
//如果没有,说明当前最早结束的会场能容纳当前的活动,更新会场的结束时间点,保证最早结束的会场最先开始下一个活动
else
{
j++;
}
}
//文件输出
ofstream ofs;
ofs.open("G:\\algorithm\\data\\4_1output.txt", ios::out);
ofs<<ans<<endl;
ofs.close();
}
输入:
输出:
复杂度分析:
排序函数sort()的时间复杂度为 O ( n l o g n ) \Omicron(nlogn) O(nlogn)
为除最早结束的活动外的活动选择会场的循环次数为 n − 1 n-1 n−1,时间复杂度为 O ( n ) \Omicron(n) O(n)
因此,整个算法的时间复杂度为 O ( n l o g n ) \Omicron(nlogn) O(nlogn)