题目 区间选点
一、有关题目(涉及算法:贪心)
1.题目来源:Acwing 905
2.题目链接: https://www.acwing.com/problem/content/907/
3.题目描述:
给定 N 个闭区间 [ai,bi]
,请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式
第一行包含整数 N
,表示区间数。
接下来 N
行,每行包含两个整数 ai,bi
,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需的点的最小数量。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
二、算法思路
1.思路
(1)将所有区间按照右端点的值,按照从小到大的顺序排列。
(2)从前到后处理每个区间。
如果,当前区间中已经包含点,则直接pass。
否则,选择当前区间的右端点。
2.对思路的解释
(1)本题目的样例具有局限性,将样例所示区间按照左端点或者右端点进行排序效果相同。
若有下面两个区间,便可易得应该按照右端点的值从小到大排序
(2)由于排序方式的原因,选取的点位于右端点可以尽可能覆盖更多的点。
(3)在没有严谨证明的情况下,我们可能会不确定选择区间中的哪个点,因此可能会想到选择区间中的任意一点。
起初,我的初版思路就是这样。
排序方式为,先将区间按长短,从小到大进行排序。
选点方式为,取任意点。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
struct interval{
int l;
int r;
int length;
}it[N];
int visited[N];
int t;
int i, j;
bool cmp(interval x, interval y){
return x.length < y.length;
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++ i){
scanf("%d%d", &it[i].l, &it[i].r);
it[i].length = it[i].r-it[i].l;
}
sort(it, it+n, cmp);
for(int i = 0; i < n; ++ i){
if(visited[i]) continue;
++ t;
visited[i] = 1;
for(int j = i+1; j < n; ++ j){
if(visited[j]) continue;
if((it[i].l>=it[j].l && it[i].l <= it[j].r) || (it[i].r>=it[j].l && it[i].r <= it[j].r)) visited[j] = 1;
}
}
printf("%d", t);
}
这中思路是有弊端的,在某些情况下会导致选点过少。因此11个测试样例,只通过了6个。
三、代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
struct interval{
int l;
int r;
}it[N];
int t, p;
bool cmp(interval x, interval y){
return x.r < y.r;
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++ i){
scanf("%d%d", &it[i].l, &it[i].r);
}
sort(it, it+n, cmp);
p = it[0].r;
++ t;
for(int i = 1; i < n; ++ i){
if(p >= it[i].l && p <= it[i].r) continue;
p = it[i].r;
++ t;
}
printf("%d", t);
return 0;
}