贪心
区间问题
点击跳转重载排序
905. 区间选点
题目描述
给定 N个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式
第一行包含整数 N,表示区间数。
接下来 N行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需的点的最小数量。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
思路
假设存在一种最优解,该解选择的点的集合为 (S),我们的贪心算法得到的点的集合为 (T)。我们可以通过数学归纳法证明,我们的贪心算法得到的解是最优的。
- 基础情况:对于第一个区间,我们选择其右端点作为第一个点,这显然是最优的,因为选择右端点可以尽可能覆盖更多后续的区间。
- 归纳步骤:假设在前 (k) 个区间上,我们的贪心算法得到的点的集合 (T_k) 与最优解的点的集合 (S_k) 是等价的(即它们覆盖的区间相同)。当考虑第 (k+1) 个区间时,如果该区间与 (T_k) 中的点没有交集,我们选择该区间的右端点作为新的点,这样可以保证在覆盖第 (k+1) 个区间的同时,尽可能覆盖更多后续的区间。因此,在加入第 (k+1) 个区间后,我们的贪心算法得到的点的集合 (T_{k+1}) 仍然是最优的。
综上所述,我们的贪心算法可以得到最优解。
将每个区间按照右端点从小到大进行排序
从前往后枚举区间,end值初始化为无穷小
如果本次区间不能覆盖掉上次区间的右端点,
ed < range[i].l
说明需要选择一个新的点,
res ++ ; ed = range[i].r;
如果本次区间可以覆盖掉上次区间的右端点,则进行下一轮循环
时间复杂度 O(nlogn)
证明
- 证明
ans<=cnt
:cnt
是一种可行方案,ans
是可行方案的最优解,也就是最小值。- 证明
ans>=cnt
:cnt
可行方案是一个区间集合,区间从小到大排序,两两之间不相交。所以覆盖每一个区间至少需要cnt个点
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range//定义一个结构体,存储区间的两个端点,Range可以随便起名字
{
int l, r;
bool operator< (const Range &W)const
{
return r < W.r;
}
}range[N];//range可以随便起名字
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;//res记录所需的点的最小数量,ed表示遍历到的最后一个区间的右端点的位置
for (int i = 0; i < n; i ++ )
if (range[i].l > ed)//如果所遍历到的最后一个区间的后面的那个区间的左端点比所遍历到的最后一个区间的左端点要大的时候,说明这俩端点是不重合的,那么就说明在遍历到的最后一个区间的那个点不在遍历到的最后一个区间的后面的那个区间里面
{
res ++ ;//所以遍历到的最后一个区间的后面的那个区间要重新给它一个所需的点
ed = range[i].r;//并将这个点定义到 遍历到的最后一个区间的后面的那个区间的右端点,在下一次循环的时候,这个区间就变成了刚才上面说的所谓的"遍历到的最后一个区间"
}
printf("%d\n", res);
return 0;
}
908. 最大不相交区间数量
题目描述
给定 N个闭区间 [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
输入格式
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示可选取区间的最大数量。
数据范围
1≤N≤105,
−109≤ai≤bi≤109
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
思路
这个题跟上一个题的思路是一样的
这个代码中的res在上一个题上是选出的点,但是这些每个点对应的区间肯定有一个区间是不一样的,所以这些点也可以代表出不相交的那些区间的个数
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range//定义一个结构体,存储区间的两个端点,Range可以随便起名字
{
int l, r;
bool operator< (const Range &W)const
{
return r < W.r;
}
}range[N];//range可以随便起名字
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].r);