贪心算法-线段覆盖类问题

文章介绍了三种不同的摄像头模型,涉及如何用最少的摄像头覆盖指定区间的问题。模型1关注单个区间覆盖,模型2处理多个小区间覆盖整个大区间,模型3探讨最大化不重叠区间的选择。每个模型都提供了思路和参考程序,主要涉及区间排序和覆盖策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模型1

模型描述

有若干个区间,问最少需要多少个点满足每个区间中至少有一个点。
像这样的模型,我们称之为摄像头1模型。

例题

例题:NKOJ P5220 摄像头1(例题被老师隐藏了,该链接可能打不开)

问题描述

有一个监控工程:在一条长度为L的笔直的公路上安装若干个摄像头,用于监控交通状况。我们可以把这条公路看作数轴[0,L]。
何老板承包了这项工程,但交管部门对摄像头的安置提出了n个要求,每个要求形如[x,y],表示在[x,y]这段区间至少要安置一个摄像头。
何老板想要用尽可能少的成本完成这项工程,因此,他想知道,最少需要安装多少个摄像头?

输入格式

第一行,一个整数n,表示有n个要求需要满足。
接下来n行,每行两个整数x和y,表示一个要求,即[x,y]这段区间至少要求一个摄像头。

输出格式

一行,一个整数,表示最少所需摄像头的个数。

样例输入

4
3 6
2 4
0 2
4 7

样例输出

2

提示

1 ≤ n ≤ 100000 1\le n\le 100000 1n100000
0 ≤ x ≤ y ≤ L ≤ 1000000000 0\le x\le y\le L\le 1000000000 0xyL1000000000

思路

每个区间都有一个左端点l和右端点r

struct camera{
	int l,r;
}c[100005];

我们按右端点从小到大排序:

bool cmp(camera a,camera b){
	return a.r < b.r;
}

接下来是重点:
我们先定义一个变量first,表示最底层的区间(如图):

接下来用for循环判断,当c[first].r < c[i].l时,说明该区间与c[first]区间不再重叠,此时应加装摄像头,并更新first
注意:摄像头数量应初始化为1。

for(int i = 0; i < n; i++){
	scanf("%d%d",&c[i].l,&c[i].r);
}
sort(c,c+n,cmp);
for(int i = 1; i < n; i++){
	if(c[first].r < c[i].l){
		ans++;
		first = i;
	}
}

参考程序

#include<bits/stdc++.h>
using namespace std;
struct camera{
	int l,r;
}c[100005];
bool cmp(camera a,camera b){
	return a.r < b.r;
}
int main(){
	int n,first = 0,ans = 1;
	scanf("%d",&n);
	for(int i = 0; i < n; i++){
		scanf("%d%d",&c[i].l,&c[i].r);
	}
	sort(c,c+n,cmp);
	for(int i = 1; i < n; i++){
		if(c[first].r < c[i].l){
			ans++;
			first = i;
		}
	}
	printf("%d",ans);
	return 0;
}

模型2

模型描述

有若干个小区间,最少选择几个小区间可以完全覆盖大区间?
像这样的模型,我们称之为摄像头2模型。

例题

例题:NKOJ P5221 摄像头2(例题被老师隐藏了,该链接可能打不开)

问题描述

NK中学里有一条长度为L的笔直道路,同学们可以把该路看作数轴,路的一段坐标为0,一段坐标为L,表示区间[0,L]

在这条路上安装有n个摄像头,每个摄像头都有一定的拍摄区间,第i个摄像头覆盖的区间为[Xi,Yi]

本着节约用电的态度,何老板想知道,最少开启几个摄像头就可以将整个这条路都置于视频监控中?请你帮他回答。

输入格式

第一行,两个整数n和L
接下来n行,每行两个整数x和y,表示一个摄像头拍摄的区域,即[x,y]这段区间被该摄像头覆盖。

输出格式

一个整数,表示最少所需开启的摄像头个数
如果无解,输出-1

样例输入 1

4 6
3 6
2 4
0 2
4 7

样例输出 1

3

样例输入 2

10 28
5 24
8 27
9 19
3 17
13 18
9 25
19 29
12 15
25 29
0 6

样例输出 2

3

提示

1 ≤ n ≤ 100000 1\le n\le 100000 1n100000
1 ≤ L ≤ 1000000000 1\le L\le 1000000000 1L1000000000
0 ≤ x ≤ y ≤ 1000000000 0\le x\le y\le 1000000000 0xy1000000000

样例说明: 选择的摄像头分别覆盖下列区间
[0,2] [2,4] [3,6]

思路

将线段按左端点从小到大排序,如果左端点相同,按右端点从大到小排序

从左往右依次讨论每条线段,优先选择右端点大的线段。

  • 记录目前已选线段中,最远能覆盖的距离nowfar
  • 讨论所有左端点<= nowfar且未被讨论的线段,记录右端点最大值maxright
  • maxright > nowfar,则nowfar = maxright。否则无解。

参考程序

#include<bits/stdc++.h>
using namespace std;
int n,l;
struct camera{
	int l,r;
}c[100005];
bool cmp(camera a,camera b){
	if(a.l == b.l){
		return a.r > b.r; 
	}else{
		return a.l < b.l;
	}
}
int solve(){
	if(c[1].l > 0){
		return -1;
	}
	int ans = 1;
	int nowfar = c[1].r;
	int maxright = c[1].r;
	if(nowfar >= l){
		return ans;
	}
	int i = 2;
	while(true){
		for(; (i <= n && c[i].l <= nowfar); i++){
			maxright = max(maxright,c[i].r);
		}
		if(maxright <= nowfar){
			return -1;
		}
		ans++;
		nowfar = maxright;
		if(nowfar >= l){
			return ans;
		}
	}
	return -1;
}
int main(){
	int tp;
	scanf("%d%d",&n,&l);
	for(int i = 1; i <= n; i++){
		scanf("%d%d",&c[i].l,&c[i].r);
	}
	sort(c+1,c+n+1,cmp);
	tp = solve();
	printf("%d",tp);
	return 0;
}

模型三

模型描述

有若干个区间,最多选几个区间使它们之间两两不重叠?
像这样的模型,我们称之为摄像头3模型。

例题

例题:NKOJ P5227 摄像头3(例题被老师隐藏了,该链接可能打不开)

问题描述

NK中学里有一条长度为L的笔直道路,同学们可以把该路看作数轴,路的一段坐标为0,一段坐标为L,表示区间[0,L]

在这条路上安装有n个摄像头,每个摄像头都有一定的拍摄区间,第i个摄像头覆盖的区间为
何老板想开启其中k个摄像头,使得这k个摄像头的拍摄区间两两没有交集,问k值最大是多少?

输入格式

第一行为一个正整数 n n n

在接下来的 n n n行中,每行有 2 2 2个数 a i , b i a_i,b_i ai,bi,描述每条线段。

输出格式

输出一个整数,为 k k k的最大值。

样例输入 1

10
7 8
1 7
0 3
4 9
0 6
5 7
0 3
7 8
7 10
4 7

样例输出 1

3

样例输入 2

10
0 3
1 4
15 19
1 2
1 3
2 5
4 7
2 4
3 8
7 12

样例输出 2

5

提示

对于 20 % 20\% 20%的数据, n ≤ 10 n\le 10 n10
对于 50 % 50\% 50%的数据, n ≤ 1 0 3 n\le 10^3 n103
对于 70 % 70\% 70%的数据, n ≤ 1 0 5 n\le 10^5 n105
对于 100 % 100\% 100%的数据, n ≤ 1 0 6 , 0 ≤ a i < b i ≤ 1 0 6 n\le 10^6,0\le a_i < b_i\le 10^6 n106,0ai<bi106

思路

  • 将所有线段的右端点从小到大排序。
  • 从左往右讨论每条线段,优先选择左端点大于上条已选线段的右端点,且右端点小的线段。

参考程序

#include<bits/stdc++.h>
using namespace std;
struct camera{
	int l,r;
}c[1000005];
bool cmp(camera a,camera b){
	return a.r < b.r;
}
int main(){
	int n,ans = 1,lastr;
	scanf("%d",&n);
	for(int i = 0; i < n; i++){
		scanf("%d%d",&c[i].l,&c[i].r);
	}
	sort(c,c+n,cmp);
	lastr = c[0].r;
	for(int i = 1; i < n; i++){
		if(c[i].l >= lastr){
			ans++;
			lastr = c[i].r;
		}
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值