鄙人才疏学浅,此中鄙陋甚多,望海涵。
题目描述
给定N个闭区间[ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式
第一行包含整数N,表示区间数。
接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需的点的最小数量。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
样例
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
这道题目要求在数轴选点,使每个区间至少包含一个点,本质上是说,相交的区间选一点即可,不相交区间须选二点,其实也就是找到最大不相交区间数量(可参考 Acwing 908)
算法1
按照右端点排序
C++ 代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10;
struct Range
{
int l,r;
bool operator< (const Range &W)const
{
return r<W.r;
}
}ranges[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
ranges[i]={l,r};
}
sort(ranges,ranges+n);
int res=0,st=-2e9;
for(int i=0;i<n;i++)
{
if(st<ranges[i].l)
{
res++;
st=ranges[i].r;
}
}
cout<< res <<endl;
return 0;
}
算法2
按照左端点排序
C++ 代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct Range
{
int l,r;
bool operator <(const Range &W)const
{
return l<W.l;
}
}range[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
range[i]={l,r};
}
sort(range,range+n);
int res=0,st=-2e9;
for(int i=0;i<n;i++)
{
if(st<range[i].l)
{
res++;
st=range[i].r;
}
else
{
st=min(st,range[i].r);
}
}
printf("%d\n",res);
return 0;
}
大家都证明得很牵强,让我也来证明一下,如果有问题,欢迎指出。疑惑点在ans>=cnt,我们可以假设有ans小于cnt,且咋们用算法求出来的区间用数组t表示。那么这个ans肯定要包含咋们算法求出来的区间。那这个ans可不可能有个区间包含了咋们求出来的区间t的两个区间呢。显然不可能,这是算法证明的关键。因为如果有这么一个区间的话,咋们贪心算法肯定能找出来,那么t区间就不是咋们算法求解出来的区间了。所以ans连我们数组t都包含不了,他还想包含全部,简直想屁吃。
https://blog.csdn.net/qq_52416556/article/details/136434922?spm=1001.2014.3001.5502最新图解
感谢大佬,思路很清晰
哈哈哈,不点个赞嘛?
同,第二种方法 为什么取min
都是对称操作,但都要掌握,因为有的题只有一个方向可以,如果只记得一种方法虽然熟练但会局限思维!
因为你左端点排序的话,一个大区间会存在多个小区间那么要想点数竟可能少,那么就选择小区间的右端点作为覆盖点,小区间可以覆盖大区间,但是不能覆盖大区间内另一个没有重复区域的小区间,所以要维护最右端点值
举个例子[3,6],[3,4],[5,6],不取min第二次得到最右端是6,和[5,6]比较满足,res=1,就错了,相当于一个大区间内部可能有多个不相同的小区间,它们并不重复,也需要一个点
按照左右端点排序有什么区别呀
嗷嗷,区别于区间合并,这里区间选点,使用小区间来向上合并大区间,区间合并反之,所以要取$min$
可以这个
优秀
贪心好难想【厚里谢】
哈哈哈,确实,不过这题倒是还好,贪心基本思路也就是排序了(顺带一提,这头像有点东西
这道题和有一道区间合并的题,有啥区别吗?我感觉都一样但是代码交上去不对acwing.com/problem/content/805/
得稍微改改,你可以再听y总讲一遍
okk,好的
这里你采用类似的方法,求解区间之间的交集,一个交集一个点,就是正确答案,区间合并求得是并集
想知道重载用在哪儿了,为什么要用重载(c++新手)
重载就是那个struct中operator那一块,重载了小于号,相当于规定了结构体排序的依据
那为什么普通的排序不可以哦
普通的排序它只能针对比较简单的东西,比如数组,比较复杂的,普通排序是不行的
取min应该是缩小公共子区间的范围
我看了大佬写的按照左端点的解法,感觉考场上我是想不出来。。我记得当时直播时y总说“按照右端点排列,不要问我为什么啊,就是这么排的”。我感觉两种方法都记的话有点容易混淆,所以感觉如果有小伙伴怕弄不清的话就记住这四道区间题的模板就行,遇到题目时根据题目所属的模板来选择按照左端点还是右端点排序,两种模板(区间选点,最大不相交区间数)按照右端点排序,两种模板(区间覆盖,区间分组)按照左端点排序。
怎么说呢?我觉得只记一个方向不会混淆,因为我是那种模板记得不怎么好的那种,我一般都按左排,特判一些情况,其实特判也比较简单。
感觉更多想出来的左端点排序,因为右端点排序要么要写重载操作符要么写仿函数,如果第一次做应该想的都是左排端点序
第二种算法,为什么else中是取min呢?
明白了 大浪数据有可能中间出现 [1,9], [2,3], [4,6] 这种情况,
左端点排序的话,从右往左扫也可以
嗯嗯,是对称的!