1. 在解决区间覆盖问题之前我们需要清楚一个概念,那就是贪心的概念,使用贪心的方法求解的是一类最优化问题的方法,它总是考虑在当前状态下局部最优(或者较优)的策略,来使全局的结果达到最优(或着较优)显然,如果采取较优而非最优的策略(最优策略不存在或者是很难想到)得到的全局结果也不是最优的,因此我们在使用贪心法来求解最优化问题的时候需要对采取的策略进行验证.
证明的思路一般是数学归纳法或者是反证法,但是在大多数情况下这个证明还是比较难的,但是我们在想到某个策略的时候难以适应理论去证明但是实际上根据我们的生活经验或者其他的信息能够表明可以由局部最优推导出全局最优的时候那么我们就可以使用贪心的策略来进行求解
2. 区间覆盖问题是典型使用贪心策略解决的一类问题,问题描述如下:
给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两没有交集,例如对于开区间(1,3)、(2,4)、(3,5)、(6,7),可以选出最多三个区间(1,3)(3,5)(6,7),他们互相之间没有交集
思路分析:
① 首先我们可以想区间的左端点越往前面那么有可能多的开区间不相交,但是我们仔细一下发现并不是这样的,因为假如一个区间左端点很靠前但是它的长度很长,在这个区间范围内包含了其他几个区间那么这样的贪心策略便是错误的,因为我们可以选择被当前区间覆盖的区间从而使得不相交的区间是最多的
② 除了开始区间之外我们还可以往结束区间上去想,假如结束的区间越往前那么不相交的区间是最多的,因为它越早结束那么可以让后面的区间的左端点可以尽可能多所以这个策略应该是正确的,但是很难去证明这个策略本身的正确性,但是我们可以发现这样做确实是可行的
③ 区间因为涉及到左右两个端点所以需要使用结构体来表示,排序可以使用C++中algorithm中的sort函数进行排序,里面传入一个自定义比较规则的函数
④ 经过上面的思考之后那么我们可以对区间的右端点进行排序,假如右端点一样那么取左端点较小的那个区间,所以可以写出下面的代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct Interval{
int x, y;
}I[maxn];
int cmp(Interval a, Interval b){
//先选择的是右端点中较小的一个
if(a.y != b.y) return a.y < b.y;
return a.x < b.x;
}
int main(void){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int start = I[0].y, ans = 1;
for(int i = 1; i < n; ++i){
if(I[i].x >= start){
ans++;
start = I[i].y;
}
}
printf("%d\n", ans);
return 0;
}
⑤ 除了上面的每次都是选择右端点比较靠前的区间之外,我们还可以采取另外一种策略就是对于所有的区间的左端点进行从大到小排序每一次都是选择的区间的右端点小于等于当前的区间的左端点,这样的策略也是可行的,具体的代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct Interval{
int x, y;
}I[maxn];
int cmp(Interval a, Interval b){
if(a.x != b.x) return a.x > b.x;
return a.y < b.y;
}
int main(void){
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int start = I[0].x, ans = 1;
for(int i = 1; i < n; ++i){
if(I[i].y <= start){
ans++;
start = I[i].x;
}
}
printf("%d\n", ans);
return 0;
}