AcWing 905.区间选点、908.最大不相交区间数量
905 区间选点
给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需的点的最小数量。
数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
贪心策略:
1.将每个区间按右端点从小到大排序
2.从前往后依次枚举每个区间
如果当前区间中已经包含点,则直接pass
否则,选择当前区间的右端点
#include<bits/stdc++.h>
using namespace std;
int n;
int a, b;
const int N = 1e5 + 10;
struct range
{
int l,r;
bool operator< (const range& x)const
{
return r<x.r;
}
}Range[N];
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &a, &b);
Range[i] = { a,b };
}
sort(Range, Range + n);//①将每个区间的 右端点 从小到大 排序
int res = 0, end = -2e9;
for (int i = 0; i < n; ++i)//②从前往后枚举每个区间
{
if (Range[i].l <= end) continue;//如果当前区间已经包含点而直接pass
else//否则,
{
res++;
end = Range[i].r;//选择当前区间的右端点
}
}
cout << res << endl;
return 0;
}
908 最大不相交区间数量
给定 N 个闭区间 [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示可选取区间的最大数量。
数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
#include<bits/stdc++.h>
using namespace std;
int n;
int a, b;
const int N = 1e5 + 10;
struct range
{
int l,r;
bool operator< (const range& x)const
{
return r<x.r;
}
}Range[N];
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &a, &b);
Range[i] = { a,b };
}
sort(Range, Range + n);//①将每个区间的 右端点 从小到大 排序
int res = 0, end = -2e9;
for (int i = 0; i < n; ++i)//②从前往后枚举每个区间
{
if (Range[i].l <= end) continue;//如果当前区间已经包含点而直接pass
else//否则,
{
res++;
end = Range[i].r;//选择当前区间的右端点
}
}
cout << res << endl;
return 0;
}
AcWing 906. 区间分组
给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示最小组数。
数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
贪心策略:
1.将所有区间按左端点从小到大排序
2.从前往后处理每个区间,判断能否将其放到某个现有的组中,如果L[i] > Max_r,则可放入现有组
1.如果不存在这样的组,则开新组,然后再将其放进去
2.如果存在这样的组,将其放进去,并更新当前组的Max_r
注意,
当判断能否将其放到某个现有的组中时,只需判断存在即可,因此我们用小根堆来实现这一点,堆顶存储的是 每个组最大右端点所形成的的集合中 的 最小值
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n;
struct range
{
int a, b;
bool operator< (const range& x)const
{
return a < x.a;
}
}Range[N];
int main()
{
cin>>n;
for(int i=0; i<n; ++i) scanf("%d%d", &Range[i].a, &Range[i].b);
sort(Range, Range+n);
priority_queue<int, vector<int>, greater<int>> heap;
for(int i=0; i<n; ++i)
{
int l = Range[i].a, r = Range[i].b;
int top = !heap.empty() ? heap.top() : -2e9;//如果堆为空就取出堆顶,会导致 segment fault,因此要写成这样
if(!heap.size()||l<=top) heap.push(r);
else
{
heap.pop();
heap.push(r);
}
}
cout<<heap.size()<<'\n';
return 0;
}
代码 + 注释
#include<bits/stdc++.h>
using namespace std;
int n, a, b;
const int N = 1e5 + 10;
struct range
{
int l, r;
bool operator< (const range& x) const
{
return l < x.l;
}
}Range[N];
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &a, &b);
Range[i] = { a,b };
}
sort(Range, Range + n);//①将所有区间按照左端点由小到大排序
priority_queue<int, vector<int>, greater<int>> pq;
//小根堆pq存储每个组中所有区间右端点的最大值max_r,其size()为分配的组数,堆顶为所有组中max_r的最小值
for (int i = 0; i < n; ++i)//②从前往后处理每一个区间判断是否能将其放到某个现有的组中
{
if (pq.empty() || Range[i].l <= pq.top())// 如果堆为空或者堆的最小右端点 >= 现在区间的左端点,则说明有交集,不能合并,需要新开一个堆
{
pq.push(Range[i].r);
}
else// 否则说明至少与最小的右端点的组没有交集,将它放到右端点最小的组里去,并更新当前组的max_r
{
//弹出压入保证pq.size()不变
pq.pop();// 弹出这个区间的最小右端点,插入当前区间的右端点,即将其更新了
pq.push(Range[i].r);
}
}
cout << pq.size() << endl;
return 0;
}
AcWing 907. 区间覆盖
给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出 −1。
输入格式
第一行包含两个整数 s 和 t,表示给定线段区间的两个端点。
第二行包含整数 N,表示给定区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需最少区间数。
如果无解,则输出 −1。
数据范围
1≤N≤10 ^5,
−10 ^9≤ai≤bi≤10 ^9,
−10 ^9≤s≤t≤10 ^9
输入样例:
1 5
3
-1 3
2 4
3 5
输出样例:
2
贪心策略:
1.将所有区间按左端点从小到大排序
2.从前往后依次枚举每个区间,在所有能覆盖 start 的区间中,选择右端点最大的区间然后将 start 更新成右端点的最大值
#include<bits/stdc++.h>
using namespace std;
int n, a, b, Start, End;
const int N = 1e5 + 10;
struct range
{
int l, r;
bool operator< (const range& x) const
{
return l < x.l;
}
}Range[N];
int main()
{
cin >> Start >> End;
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &a, &b);
Range[i] = { a,b };
}
sort(Range, Range + n);//①将所有区间按左端点由小到大排序
int res = 0;
bool ok = false;//判断是否有方案
for (int i = 0; i < n; ++i)//②从前往后依次枚举每个区间,在所有能覆盖Start的区间中,选择左端点最大的区间然后将Start更新成右端点的最大值
{
int j = i;
int ans = -2e9;//
//遍历所有左端点在Start左边的所有区间右端点的最大值是多少
while (j < n && Range[j].l <= Start)
{
//先max后j+1
ans = max(ans, Range[j].r);
++j;
}
if (ans < Start)//如果最大值都小于start则说明无解,将res赋为-1并break
{
res = -1;
break;
}
++res;
if (ans >= End)//如果最大值已经大于等于End,则表明已经能够完全覆盖给定区间了,则可以ok更新为true并break,不要忘了'=',在while之前还要使答案res+1
{
ok = true;
break;
}
Start = ans;//将Start更新为当前的最大值
i = j - 1;
}
if (ok == false) res = -1;
cout << res << endl;
return 0;
}