<数据结构学习>图——活动调度问题

目录

问题描述

问题分析

问题解决

尝试解法一

学习解法二


问题描述

假设要用很多个教室对一组活动进行调度。我们希望使用尽可能少的教室来调度所有的活动。

输入要求:

第一行为活动的个数 N(1<=N<=1 000 000) 。

接下来 N 行为 Si 和 Fi(0<=Si<Fi<=2 000 000 000) ,分别代表第 i 个活动的开始时间和结束时间。活动 i 的区间段为 [Si,Fi)

输出要求:

输出有一行 M ,为所需教室的最小数量。

问题分析

这道题是比较典型的贪心题目

我们可以借助之前有道题的思路解决。那道题说的是给一些任务的开始时间和结束时间,最多能完成多少个任务。解法是将任务按结束时间从早到晚排序。然后顺次遍历其开始时间,若下一个任务的开始时间比上一个任务的结束时间早,则不能选,否则可以选。

这道题的变化在于所有任务都要选,只是让计算最小并行数,也就是要分多少组完成一串串任务。

所以我们也可以先将所有任务按结束时间从早到晚排序。然后每次取一个任务,看看其开始时间跟各个教室最后一个任务的结束时间是否重叠,若不重叠则可以加入某教室,若重叠则要开辟新教室,最后输出教室数量即可。

问题解决

尝试解法一

首先尝试按照思路一步步实现解法。(但此法最后证明有些样例会超时)

首先定义活动结构体和教室结构体

struct activity {   //定义活动的结构体
    int start;   //成员为开始时间和结束时间
    int end;  
};  
struct room  //定义教室的结构体
{
    int endtime;  //成员为该教室最晚的活动结束时间
};

 定义一些排序函数需要的函数

struct compareEndTime {  //给优先队列排序提供一个排序依据函数
    bool operator()(const activity& a, const activity& b) {  
        if (a.end == b.end) {  
            // 如果结束时间相同,比较开始时间  
            return a.start > b.start;  
        }  
        return a.end > b.end;  //表示数小的优先级大,在队首
    }  
};  
bool compare(const room&a,const room&b) //给vector排序提供一个排序依据函数
{
    return a.endtime<b.endtime;
}

 接下来是main函数。

要注意的是每个教室存放的都是该教室最晚的活动结束时间。又因为活动是按结束时间从小到大排列的,所以当前活动aa加入的时候直接更新教室最晚活动时间即可。

int main() {  
    int n;  
    cin >> n;  
  
    // 存储所有活动  
    vector<activity> activities(n);  
    for (int i = 0; i < n; i++) 
    {  
        cin >> activities[i].start >> activities[i].end;  
    }  
  
    // 按照结束时间升序排序  
    priority_queue<activity, vector<activity>, compareEndTime> pq;  
    for (int i = 0; i < n; i++) //用优先队列存储所有活动(其实用vector也行)
    {  
        pq.push(activities[i]);  
    }  
   
    vector<room> rooms;
    // 贪心算法思想,每次选择结束时间最早的活动,并将其放入当前教室  
    room B;
    B.endtime=pq.top().end;
    pq.pop();
    rooms.push_back(B);
    while (!pq.empty()) {  
        activity aa = pq.top(); pq.pop(); //拿到活动后立即弹出
        int flag=0; 
        for(int i=0;i<rooms.size();i++) //遍历每个教室
        {
            if(aa.start>=rooms[i].endtime) //若当前活动的开始时间晚于某教室的最晚结束时间
            {
                rooms[i].endtime=aa.end; //则该活动可以加入该教室,更新教室最晚结束时间
                flag=1;
                break;
            }
        }
        if(flag==0) //如果遍历完了该活动也没找到可加入的教室
        { 
            room R;  //就新开辟一个教室,设置好教室最晚结束时间后,将该教室加入教室序列
            R.endtime=aa.end;
            rooms.push_back(R);
            sort(rooms.begin(),rooms.end(),compare); //给教室序列排序
        }
    }  
  
    // 输出所需教室的最小数量  
    cout << rooms.size() << endl;  
  
    return 0;  
}

该方法思路上是没有问题的,一些样例也能通过。可以借此学习一下优先队列及其排序的实现。

但是这种方法的问题在哪呢,我们可以考虑一下极端情况。如果活动有很多很多,并且每个活动都互相重叠,也就是说每个活动都需要一个新教室。那代码的时间复杂度将是O(n²),而n最大能取到10^6,所以有些样例肯定会TLE的。

学习解法二

由此,我学习了这篇代码

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
using namespace std;  
const int m=1e6+5;  
int n;  
int a[m],b[m];  
bool cmp(int z,int y){  
    return z<y;    
}  
int main()  
{  
    int ans=0;  
    cin>>n;  
    for(int i=0;i<n;i++)  
    {  
        scanf("%d %d",&a[i],&b[i]);  
    }  
    sort(a,a+n,cmp); //活动开始时间   
    sort(b,b+n,cmp); //活动结束时间   
    int cal=0;  
    for(int i=0,j=0;i<n;)  
    {  
        if(a[i]<b[j])  
        {  
            cal++;  
            i++;  
        }  
        else //  
        {  
            cal--;  
            j++;   
        }  
        if(ans<cal) ans=cal;  
    }  
    cout<<ans<<endl;  
    return 0;  
}  

虽然还没没明白为什么开始时间和结束时间可以分开排,但是可以作为拓展和了解解题方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值