7-2 会场安排问题 (20分)

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

输入格式:

第一行有 1 个正整数k,表示有 k个待安排的活动。 接下来的 k行中,每行有 2个正整数,分别表示 k个待安排的活动开始时间和结束时间。时间 以 0 点开始的分钟计。

输出格式:

输出最少会场数。

输入样例:

5
1 23
12 28
25 35
27 80
36 50 

输出样例:

3

算法:

错误解法:

按照结束时间排序,然后开始放活动,这样贪心可以使每次放的活动最多。

这样做是错的,这样只能保证第一次放的活动是最多的,但是具备有后效性,不能使全局最优。网上的很多解法都是这个,是错误的。

比如数据:

6
90 98---1
8 32----2
18 48---3
16 82---4
83 84---5
39 89---6

  

这组数据的答案是3,我们把活动按照1-n编号,那么分别用三个会场装(2, 6)、(4, 5)、(3, 1),就能完成任务,答案是3。

但是如果按照贪心排结束时间的做法,答案是4,在第一次拿的时候我们拿了很多,3个,但是这也导致后面的活动都不得不单独开一个会场,导致答案为4,不是最优解。这也就是典型的局部最优不能带来全局最优。

附上错误代码:

#include <iostream>
using namespace std;

typedef struct 
{
    int start;
    int end;
}Event;


int main(){
    int k;                                   //表示有 k个待安排的活动
    cin>>k;
    Event event[k+1];
    for(int i=1;i<=k;i++){
        cin>>event[i].start>>event[i].end;   //活动开始时间和结束时间
    }
    
    for(int i=1;i<=k;i++){                  //event数组按活动的结束时间从小到大排序 
        int min=event[i].end,min_id=i;      //冒泡 
        for(int j=i+1;j<=k;j++){   
            if(event[j].end<min) {
                min = event[j].end;
                min_id = j;
            }
        }
        swap(event[i],event[min_id]);
    }
    
    int room[k+1]={0};                    // 会场,room[i]记录活动结束的时间,即表示第i个会场开始空闲的时间 
                                          // 初始化为0,表示未使用,若不是0则代表该会场已经被使用过 
    for(int i=1;i<=k;i++){
        for(int j=1;j<=k;j++){
            if(room[j]<=event[i].start) 
            {
                room[j]=event[i].end;
                break;
            }
        }    
    }

    int count=0;                          //统计room数组非0单元个数,即最小使用数 
    for(int i=1;i<=k;i++){    
        if(room[i]==0) break;
        count++;
    }
    
    cout<<count<<endl;
}

稍微改一下就好

正确代码:

#include <iostream>
using namespace std;

typedef struct 
{
    int start;
    int end;
}Event;


int main(){
    int k;                                   //表示有 k个待安排的活动
    cin>>k;
    Event event[k+1];
    for(int i=1;i<=k;i++){
        cin>>event[i].start>>event[i].end;   //活动开始时间和结束时间
    }
    
    for(int i=1;i<=k;i++){                  //event数组按活动的开始时间从小到大排序 
        int min=event[i].start,min_id=i;    //冒泡 
        for(int j=i+1;j<=k;j++){   
            if(event[j].start<min) {
                min = event[j].start;
                min_id = j;
            }
        }
        swap(event[i],event[min_id]);
    }
    
    int room[k+1]={0};                    // 会场,room[i]记录活动结束的时间,即表示第i个会场开始空闲的时间 
                                          // 初始化为0,表示未使用,若不是0则代表该会场已经被使用过 
    for(int i=1;i<=k;i++){
        for(int j=1;j<=k;j++){
            if(room[j]<=event[i].start) 
            {
                room[j]=event[i].end;
                break;
            }
        }    
    }

    int count=0;                          //统计room数组非0单元个数,即最小使用数 
    for(int i=1;i<=k;i++){    
        if(room[i]==0) break;
        count++;
    }
    
    cout<<count<<endl;
}

 下图重叠的地方表示活动之间不兼容,即不能在同一会场进行

最大重叠数 = 最小会场数

原文地址:https://www.cnblogs.com/lvjingyuan/p/13933071.html