算法分析与设计-会场安排问题(贪心)(通俗易懂,附源码和图解,含贪心选择性质和最优子结构性质的证明)(c++)

4-1 会场安排问题

(一)题目

问题描述

假设在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排。(这个问题实际上是著名的图着色问题。若将每个活动作为图的一个顶点,不相容活动间用边相连。使相邻顶点有着不同颜色的最小着色数,相当于要找的最少会场数。)

算法设计

对于给定的 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=Ee的最优解,该命题的证明可用反证法,证明如下:

假设 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}
算法分析与设计-会场安排问题(贪心)(通俗易懂,附源码和图解,含贪心选择性质和最优子结构性质的证明)(c++)_第1张图片
总共需要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();
}
结果示例

输入:

算法分析与设计-会场安排问题(贪心)(通俗易懂,附源码和图解,含贪心选择性质和最优子结构性质的证明)(c++)_第2张图片

输出:

在这里插入图片描述

(三)总结

复杂度分析:

排序函数sort()的时间复杂度为 O ( n l o g n ) \Omicron(nlogn) O(nlogn)

为除最早结束的活动外的活动选择会场的循环次数为 n − 1 n-1 n1,时间复杂度为 O ( n ) \Omicron(n) O(n)

因此,整个算法的时间复杂度为 O ( n l o g n ) \Omicron(nlogn) O(nlogn)

你可能感兴趣的:(算法设计与分析,算法)