题目描述
给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些区间两两没有交集。
看到这一个题我们需要现在纸上画一画,有3种情况
1) 区间相交。2)区间不相交。3)区间包含(其实也算区间相交)。
我们会发现,当区间相交时,我们用小区间就行了,能保证我们找出的不相交的区间最多。
然后我们将所有区间的左端点也就是 x 从大到小排序,然后每次找出右端点在这个左端点左面的就是一个,然后选取这个区间的左端点作为下次判断的标准,当左端点相同时,按照右端点 y 从小到大排序,就是我们检查时先检查(左端点相同时)右端点小的(正好可以去去除区间包含的大区间)。
这就是整体思路了,我们就要看代码了。
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 105;
class Inteval{
public:
int x, y;
}I[maxn];
bool cmp(Inteval a, Inteval b){
if(a.x != b.x)
return a.x > b.x; //左端点不同的,从大到小排序
else
return a.y < b.y; //左端点相同的,从小到大排序
}
int main(){
int n;
while(scanf("%d", &n), n != 0){
for(int i = 0; i < n; i++){
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int ans = 1; //至少就有一个区间。
int LastX = I[0].x;
for(int i = 1; i < n; i++){
if(I[i].y <= LastX){ //思考何时加= 何时不加=
LastX = I[i].x;
ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
然后反向一波 ,如果我们是以右端点作为端点,我们应怎么写这个代码
思路,我们如果用右端点y,那么我们排序是右端点y 从小到大的顺序排序的,如果右端点相同的话,我们选择左端点大的,我们先以第一个作为基础,然后遍历,找到第一左端点在右端点右边的 ans++ ,然后以这个区间的右端点作为基础继续比较。
代码如下
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 105;
class Inteval{
public:
int x, y;
}I[maxn];
bool cmp(Inteval a, Inteval b){
if(a.y != b.y)
return a.y < b.y; //右端点不同的,从小到大排序
else
return a.x > b.x; //右端点相同的,从大到小排序
}
int main(){
int n;
while(scanf("%d", &n), n != 0){
for(int i = 0; i < n; i++){
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int ans = 1; //至少就有一个区间。
int LastY = I[0].y;
for(int i = 1; i < n; i++){
if(I[i].x >= LastY){
LastY = I[i].y;
ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
第2种思路
1)按照左端点(开始时间升序排序)。
2)然后开始遍历所有区间,(1)先看下个区间的左端点是否在当前区间右端点的右边,如果在右边,ans++,(2)如果在右边(下个区间的左端点在这个区间内),并且当前选择选择区间的右端点大于下个区间的右端点那就换区间进行比较,答案不+1,(就是区间包含的情况)
/*区间贪心*/
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 120;
struct type {
int x, y;
};
type I[maxn];
bool cmp(type a, type b) {
if (a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}
int main() {
int n;
while(scanf("%d", &n), n != 0) {
for (int i = 0; i < n; ++i) {
scanf("%d%d", &I[i].x, &I[i].y);
}
sort(I, I + n, cmp);
int ans = 1, lastX = I[0].x, lastY = I[0].y;
for(int i = 1; i < n; ++i) {
if(lastY <= I[i].x) {
ans ++;
lastX = I[i].x;
lastY = I[i].y;
} else {
if (lastY > I[i].y) {
lastX = I[i].x;
lastY = I[i].y;
}
}
}
printf("%d\n", ans);
}
return 0;
}
然后我们延伸一下
出个变形题,同学们注意,老师要变形了。
区间选点问题:给出N个闭区间 [ x, y ], 求至少需要多少个点才能使每个闭区间中至少存在一个点。
看题先干什么???
对了,画图,我们先来动动手,咦,好像给上面的有一定的相似之处,然后我们一跺脚。深吸一口气,这智障的题,
1)区间相交,2)区间不相交。
不就这两种情况么,如果相交那就一个点,不相交则最少2个点嘛,就这么简单???
那代码我们怎么写呢?,不还是判断有多少个不相交的区间么,这也太简单了把,然后经过一系列分析发现这个不是闭区间 我们只需要将
I[i].y <= LastX //等号去掉不就行了么
I[i].y < LastX
这就很简单了,那我们总结下,
看到区间比大小,我们第一个应该想起这个题,应该就是这个题的本质,根据端点去判断,这就是区间题的突破口。
然后总结一下贪心
贪心是用来解决一类最优化问题,一般就是问最多,最少,最大值,最小值,最短,最长,只要这个题考最。。。。或者问怎么才能使。。。。。我们就可以考虑贪心算法。
由局部最优策略来推得全局最优结果的算法思想。
贪心算法适用的问题一定满足最优子结构。