线段覆盖问题
1.线段不覆盖问题
给出 \(n\) 个线段,选择尽量多的线段使得选择的线段相互之间无重叠,问最多可以选多少条线段。
解析
考虑贪心,将线段按右端点从小到大排序,如果这条线段的左端点大于上一条线段的右端点那就选择这条线段。
为什么这么贪是对的呢,因为将右端点排序可以使右边剩余的空间尽量大,那么剩余的选择就可能会更大,而且每个线段对答案贡献都为一,我可以剩下更多的空间,我们价值都为一,那我为什么要给你(贪心真残酷,但就是弱肉强食)。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
struct ss{
int s,t;//s:开始,t:结束
}a[1000005];
bool cmp(ss g,ss h){
return g.t<h.t;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].s>>a[i].t;
}
sort(a+1,a+n+1,cmp);
int last=0,ans=0;//last:上一个选择线段的右端点
for(int i=1;i<=n;i++){
if(last<=a[i].s){//因为不能重叠
ans++;
last=a[i].t;//更新
}
}
cout<<ans;
return 0;
}
2.线段完全覆盖问题
题面
给定一个长度为 \(m\) 的区间,再给出 \(n\) 条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖。
解析
考虑贪心,将线段按左端点排序,用 \(ml\) 表示已选择的线段中右端点最右的点,用 \(mr\) 在左端点不超过 \(ml\) 时能遍历到的最右的端点,如果下一个点的左端点没有超过 \(ml\),我们就不断地找右端点最大的点(max(a[i].r , mr)),下一个点左端点超过 \(ml\) 的时候(因为如果不选出一个选段的话将会造成无法完全覆盖),我们就在遍历过的点里选用 \(mr\),然后将 \(ml\) 设为 \(mr\),答案数增加。
注意一些边界问题!!代码里有详写。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
struct ss{
int s,t;
}a[1000005];
bool cmp(ss g,ss h){
if(g.s!=h.s){//要右端点也递增
return g.s<h.s;
}
else{
return g.t<h.t;
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].s>>a[i].t;
}
sort(a+1,a+n+1,cmp);
int ml=b[1].t,mr=a[1].t,ans=0;
a[n+1].s=1e8;//最后的线段要选
for(int i=1;i<=n;i++){
mr=max(mr,a[i].t); //选出最大右端点
if(a[i+1].s>ml){//如果不选出一个将会造成无法覆盖
ml=mr;//更新边界
ans++;
}
}
cout<<ans;
return 0;
}