算法学习笔记(9)——区间合并
区间合并
区间合并问题也是一个贪心问题,由于比较常用所以单独拿出来。区间合并的解决方法是,把所有区间按照左端点 \(l\) 从小到大排序,然后维护一个当前正在处理的区间 \([st,ed]\),如果遍历到区间和维护的区间有交集,就合并(能合则合),没有交集的时候,当前维护的区间就变成这个遍历到的区间。
这里按照左端点排序好之后,每次遍历到的区间和当前区间 \([st,ed]\) 只会有三种状态:
情况(1)是 \(l \leq ed\) 并且 \(r \leq ed\),这个时候可以和当前区间合并,合并后的当前区间就是 \([st,ed]\)。
情况(2)是 \(l \leq ed\) 并且 \(r > ed\),这个时候可以和当前区间合并,合并后的当前区间就是 \([st,r]\)。
情况(3)是 \(l > ed\),这个时候不能和当前区间合并,当前区间要从 \([st,ed]\) 变成新的 \([l,r]\)。
所以只要在 \(l \leq ed\) 的时候进行区间合并,合并就是对 \(ed\) 和 \(r\) 取一个较大值作为合并后的当前区间的右端点。在 \(l > ed\) 的时候没法合并,而是把维护的当前区间变成这个正在遍历的区间就可以了。
本题中我们用pair<int, int>
类型存储每一个区间,题目所给的范围是从 \(-1e9\) 到 \(1e9\),所以我们选择一个不在此范围内的数字进行初始化,便于我们在第一次遍历区间左右端点时进行判断。
每次在判断新来的区间左端点 \(seg.first\) 大于待合并区间的右端点 \(ed\) 时,说明前一区间合并完毕,加入最终的答案集合中。如果枚举的是第一个区间,则不需要加入集合,修改当前的起始点即可。
在枚举完所有区间之后还要进行一次判断,此时如果当前维护的区间 \([l,r]\) 还没有被加入答案集合中,也就是所有区间都合并为了一个区间的情况,我们还需要把这个区间加入到最终答案集合中去。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
vector<PII> segs;
void merge(vector<PII> &segs)
{
vector<PII> res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs) {
if (seg.first > ed) {
if (st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else {
ed = max(ed, seg.second);
}
}
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
int main()
{
int n;
cin >> n;
while (n -- ) {
int l, r;
cin >> l >> r;
segs.push_back({l, r});
}
merge(segs);
cout << segs.size() << endl;
return 0;
}