【每日算法】基础算法——区间选点(八十五)

题目内容

给定N个闭区间[ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。

位于区间端点上的点也算作区间内。

输入格式

第一行包含整数N,表示区间数。

接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示所需的点的最小数量。

数据范围

1≤N≤10^5 ,
−10^9≤ai≤bi≤10^9

输入样例

3
-1 1
2 4
3 5

输出样例

2

题解

贪心的特点是每次选择均选择在当前情况下的最好选择,没有对全局有思考,因此算法在总体上有些短视。
思路如下:
1、将每个区间按右端点从小到大排序
2、从前往后依次枚举每个区间。
如果当前区间已经被覆盖,则直接pass。否则选择当前区间的右端点。

可以证明,根据这种方法一定能够得到最优解。
假设,ans是我们最后要求的答案,cnt是我们求出的可行点数的和。
首先,ans≤cnt,因为ans是可行点数中最小值。
然后,又因为cnt的选择是选择右端点,且若区间覆盖则会被pass掉,这就导致每个点之间均无任何重叠的可能性,因此会找到cnt个点对应的相互没有交集的区间,因此cnt≤ans,因此可以证明,cnt=ans。

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
struct Range
{
int l, r;
bool operator< (const Range &W)const
{
return r < W.r;
}
}range[N];

int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].r);

sort(range, range + n);

int res = 0, ed = -2e9;
for (int i = 0; i < n; i ++ )
if (range[i].l > ed)
{
res ++ ;
ed = range[i].r;//上一次选中的区间的右端点的坐标。当前备选区间的左端点如果小于等于ed,那么说明两个区间有交集,那么上一次选中区间的右端点也能包含在当前备选区间里,所以这个区间不必产生新的点就可以满足题意,所以被舍掉,不用考虑。只有左端点大于ed的区间才会产生新的点,只有遇到这样的区间计数器才会加1,同时ed要更新。
}

printf("%d\n", res);

return 0;
}
Author: Frederic Niu
Link: https://www.fredericniu.cn/2021/03/01/【每日算法】基础算法——区间选点(八十五)/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
我的公众号