题目 AcWing906.区间分组
描述
给定 N N N 个闭区间 [ a i , b i ] [ai,bi] [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。
输入
第一行包含整数 N N N,表示区间数。
接下来 N N N 行,每行包含两个整数 a i , b i ai,bi ai,bi,表示一个区间的两个端点。
输出
输出一个整数,表示最小组数。
数据范围
1
≤
N
≤
1
0
5
1≤N≤10^{5}
1≤N≤105
−
1
0
9
≤
a
i
≤
b
i
≤
1
0
9
−10^{9}≤ai≤bi≤10^{9}
−109≤ai≤bi≤109
样例输入
3
-1 1
2 4
3 5
样例输出
2
思路
这道题用到一个很重要的数据结构priority_queue
,需要包含头文件<queue>
。优先队列具有队列的所有特性,包括队列的基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
优先权队列有以下基本操作:
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
定义:priority_queue<Type, Container, Functional> heap;
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque
等等,但不能用 list。STL里面默认用的是vector
),Functional 就是比较的方式。当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。一般定义方式为:
1 //升序队列,小顶堆
2 priority_queue <int, vector<int>, greater<int>> heap;
3 //降序队列,大顶堆
4 priority_queue <int, vector<int>, less<int>> heap;
本道题的贪心准则:按照区间左端点从小到大排序。
struct Range
{
int l, r;
bool operator<(const Range& R)const
{
return l < R.l;
}
}range[N];
核心代码分析
我们按左端点从小到大遍历,再将每个区间的左端点 range[i].l
与每一组的最右端点的最小值 heap.top()
比较:
若 heap.top() >= range[i].l
或 heap
为空,则说明没有一组集合可以包含该区间,因此建立新的分组 heap.push(range[i].r)
;
若 heap.top() < range[i].l
,说明有分组可以包含该区间,我们默认把该区间加入到第一个分组中(第一个分组的右端点值较小,这样可以使每个分组包含尽量多的区间),然后从 heap
中弹出第一个分组的右端点值,并用 range[i].r
更新第一个分组的右端点。
代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
struct Range
{
int l, r;
bool operator<(const Range& R)const
{
return l < R.l;
}
}range[N];
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d%d", &range[i].l, &range[i].r);
sort(range, range + n);//将区间数组按左端点从小到大排序
int res = 0;
//定义小根堆,用来存放每一组区间的最右端点
priority_queue<int, vector<int>, greater<int>> heap;
for (int i = 0; i < n; i++)
{
if (heap.empty() || heap.top() >= range[i].l)
{
res++;//建立新的分组
heap.push(range[i].r);
}
else
{
//弹出第一组右端点的值并更新
heap.pop();
heap.push(range[i].r);
}
}
printf("%d", res);
return 0;
}
欢迎交流与指正~