思路
-
如果一个区间的左端点比最小组的右端点要小,
ranges[i].l<=heap.top()
, 就开一个新组heap.push(range[i].r)
; -
如果一个区间的左端点比最小组的右端点要大,则放在该组,
heap.pop(), heap.push(range[i].r);
每组去除右端点最小的区间,只保留一个右端点较大的区间,这样heap
有多少区间,就有多少组。
例子
俩个问题
-
如何判断某一个组当中,是否存在一个区间和我当前区间有交集呢?
- 只需要对每一个组存一个max_r就可以(他表示这个组中所有区间的右端点的最大值)
- 要是有多个组满足条件的话,就随便挑选一个放进去
-
如何快速判断它的max_r是否小于L[i]的呢?
- 用小根堆,如果所有组的max_r的最小值满足这个条件,就找到了这样的组,如果最小值都大于等于L[i]的话,不存在这样的组(有交集)
- 每次就是判断最小的max_r小于L[i]就可以
- 动态维护最小值,可以使用堆来做(小根堆),如果动态维护最大值,可以使用大根堆来做
注意
- 对于小根堆来书,当把根节点去掉后,他会自动从根中选择一个最小的当做根节点
题目
代码
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l, r;
bool operator< (const Range &w) const
{
return l < w.l; //按照左端点进行排序
}
}range[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i ++)
{
int l, r;
cin >> l >> r;
range[i] = {l, r};
}
sort(range, range + n);
priority_queue<int, vector<int>, greater<int>> heap; //定义小根堆
//从前往后处理每一个区间,用小根堆维护,所有组的右端点的最大值
//我们的小根堆始终保证所有组中的最小的右端点为根节点
for (int i = 0; i < n; i ++)
{
auto r = range[i];
//如果当前队列为空,或者区间的端点小于小根堆的根(当前组的最小右端点)
if (heap.empty() || heap.top() >= r.l) heap.push(r.r); //说明有交集。自己开一个组
//如果大于组当中的最小右端点,说明它至少肯定和这个组没有交集,没有交集那就把它归到这一组
else
{
//既然大于我们小根堆的根,也就说明把它该归到小根堆根所代表的这一组,根就失去了作用
//我们将跟去掉,用新的t.r来放入小根堆里,小根堆替我们自动找到所有组当中为所有组的最小右端点,并作为新根
int t = heap.top();
heap.pop();
heap.push(r.r); //把当前新的右端点加到堆里面去
}
}
cout << heap.size(); //我们就是用size来表示的组的
return 0;
}