1)最大不相交区间数(区间选点)
- 翻译一下题目,也就是说给定 n n n个闭区间,问能从中选出多少个区间使其互不相交
- 贪心策略:新建一个结构体存储区间的左端点和右端点,将这 n n n个区间按照右端点从小到大排序,遍历所有的区间,如果当前遍历到的区间左端点的值大于了上一个区间的右端点的值,说明此时区间没有重合,则区间个数 + 1 +1 +1
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
// 解题思路:
const int n=1e5+5; // 区间总数
// 区间
struct range {
int l; // 左端点
int r; // 右端点
}range[n];
// 按照区间右端点从小到大排序
bool cmp(range a,range b) {
return a.r<b.r;
}
int n;
int main() {
cin>>n;
// 输入规模大于1e5,推荐使用速度更快的scanf
for(int i=1;i<=n;i++) {
scanf("%d%d",&range[i].l,&range[i].r);
}
// 计算答案
int res=0;
int front_r=int_min; // i=1时至少有一个,所以res从0开始
sort(range+1,range+1+n,cmp); // 排序
for(int i=1;i<=n;i++) {
if(range[i].l>front_r) {
res++;
front_r=range[i].r; // 更新上一个区间的右端点
}
}
cout<<res;
return 0;
}
2)区间分组
- 翻译一下题目,现在有 n n n个区间,问最少分多少组,能让这些分组中的所有区间两两之间包括端点都没有交集
- 贪心策略:将所有区间按照左端点从小到大排序,从前往后处理每个区间,判断是否能把这个区间放到现有的某个分组中,即判断
r
a
n
g
e
[
i
]
.
l
>
某一分组最大右端点
range[i].l \ > \ 某一分组最大右端点
range[i].l > 某一分组最大右端点
- 如果存在这样的分组(与某个分组间没有交集),则将区间 $ i $ 放进去,并且更新这个分组的最大右端点(注意,如果与多个分组都有交集则可以放到任意一个分组中,不影响结果,可自行模拟证明)
- 如果不存在这样的分组(与每个分组间都有交集),则开一个新组,再把区间 i i i 放进去
- 在判断当前区间是否与某一分组有交集时,只需要与所有分组中最小的分组最大右端点进行比较即可,因为这是最有可能满足 r a n g e [ i ] . l > 某一分组最大右端点 range[i].l \ > \ 某一分组最大右端点 range[i].l > 某一分组最大右端点 的情况,对于所有分组中最小的分组最大右端点,只需要用一个小根堆来维护即可
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
// 解题思路:
// 将所有区间按照左端点从小到大排序,从前往后遍历每个区间,判断能否将其放进某个现有的组中
// 用堆来维护每个分组的max_r,
const int n=1e5+5;
struct range {
int l;
int r;
}range[n];
bool cmp(range a,range b) {
// 按照左端点从小到大排序
return a.l<b.l;
}
int main() {
int n;
cin>>n;
for(int i=1;i<=n;i++) {
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range+1,range+1+n,cmp);
priority_queue<int,vector<int>,greater<int>>heap; // greater升序原则小根堆,维护每个分组的max_r的最小值
// 枚举每个区间
for(int i=1;i<=n;i++) {
// 如果每个分组中最小的max_r都比当前区间的左端点大(说明有交集,不能放入该区间中)
if(heap.empty() || heap.top()>=range[i].l) {
heap.push(range[i].r); // 新开一个分组
} else {
// 如果无交集,说明可以放入该分组中,此时弹出再更新这个分组的最大值
heap.pop(); // 因为rnage[i].r一定比heap.top()更大,不然怎么可能无交集
heap.push(range[i].r);
}
}
// 小根堆的大小就是分组的个数
cout<<heap.size()<<endl;
return 0;
}
3)区间覆盖
- 翻译一下题目,···,这道题没什么可翻译的
- 贪心策略:将所有区间按照左端点从小到大排序,从前往后依次枚举每个区间,在所有能覆盖 s t a r t start start 的区间中选择一个右端点最大的区间,然后将 s t a r t start start 更新成为右端点的最大值,继续向后覆盖,如果所有区间都遍历过了但并没有覆盖到 e n d end end 的话说明无法完全覆盖,则输出 − 1 -1 −1
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
// 解题思路: 双指针+贪心
const int n=1e5+5;
struct range {
int l,r; // 左端点,右端点
// 重载运算符从小到大排序
bool operator<(const range &w)const {
return l<w.l;
}
}range[n];
int main() {
int st,en;
cin>>st>>en;
int n;
cin>>n;
for(int i=1;i<=n;i++) {
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range+1,range+1+n);
int res=0; // 存储答案
bool success=false;
// 遍历所有区间
for(int i=1;i<=n;i++) {
// front_r可理解为当前覆盖到的区域
int j=i,front_r=int_min; // 双指针,j从第i个区间开始找
// 取所有左端点能覆盖st中右端点最大的区间,覆盖到这个位置
while(j<=n && range[j].l<=st) {
front_r=max(front_r,range[j].r);
j++;
}
// 如果front_r仍小于st,说明线段不可被已有区间覆盖
if(front_r<st) {
break;
}
// 如果找得到,答案+1
res++;
// 已经完全覆盖
if(front_r>=en) {
success=true;
break;
}
// 否则更新i和j
st=front_r; // 迭代st,后面的区间继续和st比
i=j-1; // 双指针,加速遍历
}
if(!success) res=-1;
cout<<res;
return 0;
}
发表评论