区间分组 | 青训营

203 阅读2分钟

题目描述

给定N个闭区间[ai,bi][a_i, b_i],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小,输出最小组数。

输入格式

第一行包含整数N,表示区间数。接下来NN行,每行包含两个整数ai,bia_i, b_i,表示一个区间的两个端点。

输出格式

输出一个整数,表示最小组数。

数据范围

1N1051 \leq N \leq 10^5
109aibi109-10^9 \leq a_i \leq b_i \leq 10^9

输入样例

3
-1 1
2 4
3 5

输出样例

2

思路

贪心

  1. 首先按照区间的左端点大小,从小到大排序
  2. 依次检查每一个区间,如果此区间可以加入到一个组中就加入进去,如果全部都不能加入,就新开一个组

证明

  • anscntans \leq cnt。由于算出来的cntcnt一定是一个合法的解(在每一个组中没有相交的区间),最终答案肯定小于等于这个解
  • cntanscnt \leq ans。我们考虑已经有cnt1cnt - 1组的情况,如果此时其他的组都不满足合并的条件,
    必须新开一个组,由于是按照左端点排序扫描的,其他组拥有最大的右端点的区间的左端点一定
    在此区间ll的左侧,而又无法合并,说明其他组的最大的右端点在ll右侧,最后说明,
    cntcnt个点都拥有ll这个点,至少要cntcnt个组分开,所以cntanscnt \leq ans

代码

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

const int N = 100010;
struct Range{
	int l, r;
	// 重载运算符,对range排序
	bool operator< (const Range &w) const{
		return l < w.l;
	}
}range[N];

int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++){
		int a, b;
		scanf("%d%d", &a, &b);
		range[i] = {a, b};
	}
	// 排序
	sort(range, range + n);
	// 创建小跟堆,动态维护所有区间中maxr的最小一个区间,如果连最小的右端点无法满足,那就不可能进入任何一个组中
	priority_queue<int, vector<int>, greater<int>> heap;
	for(int i = 0; i < n; i ++){
		int l = range[i].l, r = range[i].r;
		// 考虑heap为空的情况,此时已经不能加入任何一个组中
		if(heap.empty() || l <= heap.top()){
			heap.push(r);
		}else{	// 直接加入第一个组中,弹出最顶的那个区间的值,加入此区间的r
			heap.pop();
			heap.push(r);
		}
	}
	// 最后堆的大小就是组的数量
	printf("%d", heap.size());
	return 0;
}