目录
问题描述
假设要用很多个教室对一组活动进行调度。我们希望使用尽可能少的教室来调度所有的活动。
输入要求:
第一行为活动的个数 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;
}
虽然还没没明白为什么开始时间和结束时间可以分开排,但是可以作为拓展和了解解题方法。