线段覆盖问题

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;
}
posted @ 2024-08-29 15:13  sad_lin  阅读(18)  评论(0编辑  收藏  举报