题目描述
给定 $n$ 个区间 $[li,ri]$,要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3]和[2,6]可以合并为一个区间[1,6]。
样例
5
1 2
2 4
5 6
7 8
7 9
3
算法
(贪心 + 区间合并)
- 刚做的时候,一直苦恼,多组
l, r
,要怎么存储,一开始没想到使用vector
,一直把它当数组,其实理解成容器更好些,通过这道题,对于vector
和容器的理解更深了些。 - 对于区间问题,要进行按左端点从小到大排序,所以使用
sort()
。 - 核心是
merge()
函数,有3个地方要注意——- 如果判断当前维护的区间
[st, ed]
和枚举的区间seg
没有交集,就把[st, ed]
放进答案(初始-2e9的st和ed除外),初始为-2e9的情况会自动跳到第2句代码,把st和ed更新成当前的seg的左右端点。如果不是初始st和ed,在将当前维护的st和ed放入ans中后,也会把当前维护的区间st~ed
更新成当前枚举区间的左右端点。 - 如果当前维护的区间
[st, ed]
和枚举的区间seg
是有交集的,那么我们只需要更新ed
和seg
的右端点的最大值就好了,因为seg
的左端点不可能覆盖st
,最多是重合st = seg.first
。同时,又是在合并区间,所以取一个ed和seg右端点的max值就好了。 - 最后,最后一个区间,如果当前维护的区间
[st, ed]
和最后一个seg不相交,[st, ed]
被放入答案中后,st ed
被更新成最后一个区间的左右端点,但是还没有放入ans中,所以在整个for()
结束后,要再来一次ans.push_back()
。但前提是,为了防止题目的输入数据中是空的,一个区间都没有,那么st ed
就不会被更新,一直是-2e9
,我们不能将这种放入ans中,所以判断一下,if(st != -2e9)
,如果不是,就再push_back
一次。最后把答案ans
拷贝给segs
。
- 如果判断当前维护的区间
时间复杂度
$O(n)$
C++ 代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100010;
typedef pair<int, int> PII;
vector<PII> segs;
void merge(vector<PII> &segs)
{
vector<PII> ans;
int st = -2e9, ed = -2e9;
for(auto seg : segs)
if(ed < seg.first)
{
if(st != -2e9 && ed != -2e9) ans.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else ed = max(ed, seg.second); // **2
// 防止输入的是空的,一个区间都没有
if(st != -2e9) ans.push_back({st, ed}); // **3 **4
segs = ans;
}
int main()
{
int n;
scanf("%d", &n);
while(n --)
{
int l, r;
scanf("%d%d", &l, &r);
segs.push_back({l, r});
}
sort(segs.begin(), segs.end()); // **1
merge(segs);
cout << segs.size() << endl;
return 0;
}