题目地址:
https://leetcode.com/problems/meeting-rooms-ii/
给定一系列会议的开始结束时间,问至少安排多少个会议室足够。
这个问题相当于问,若干个左闭右开的区间 [ a i , b i ) [a_i,b_i) [ai,bi),至少能分为多少个组,使得组内区间两两不相交。法1实际上是贪心法,证明参考https://blog.csdn.net/qq_46105170/article/details/113734794(那题是闭区间,但证明是类似的)。
法1:优先队列。先将会议按起始点排序。接着遍历会议,每次进堆的时候就相当于新开一个会议室,我们考虑一下什么情况要新开一个会议室。显然就是,要么堆空,也就是当前没会议室,这个时候要新开一个;要么当已经入堆的会议里,最早结束的那个仍然没结束,结果新的会议开始时间比最早结束的会议的结束时间还要早,那就只好新开一个会议室了。所以,可以开一个最小堆,谁结束点在前谁优先。当遍历到新的会议,新的会议开始时间小于已经安排好会议室的会议里最早结束的结束时间,这个时候就新开一个会议室,将其入堆;否则的话,将那个最早结束的会议出堆,将新会议进堆。代码如下:
class Solution {
public:
int minMeetingRooms(vector<vector<int>>& is) {
sort(is.begin(), is.end());
auto cmp = [](auto& p1, auto& p2) { return p1[1] > p2[1]; };
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> heap(cmp);
for (auto& ii : is) {
// 如果最早结束的那个会议可以结束掉,让新会议用它的会议室,那就将那个会议出堆,将新会议进堆
if (heap.size() && heap.top()[1] <= ii[0]) heap.pop();
// 否则就直接开个新会议室,将新会议进堆
heap.push(ii);
}
return heap.size();
}
};
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。
法2:扫描线。可以开一个map,key是时间点,value是这个时间点对应的净新开的会议数(“净”的意思是,如果有新会议开始,就计数 1 1 1,如果是旧会议结束,就计数 − 1 -1 −1)。然后对时间点按顺序扫描,用cnt记录每个时间点正在开的会议数。这个cnt达到的最大值即为所求。代码如下:
class Solution {
public:
int minMeetingRooms(vector<vector<int>>& is) {
map<int, int> mp;
for (auto& p : is) mp[p[0]]++, mp[p[1]]--;
int res = 0, cnt = 0;
for (auto &[k, v] : mp) cnt += v, res = max(res, cnt);
return res;
}
};
时空复杂度与上同。