WEEK3 周记 作业B题——贪心算法_区间选点
一、题意
1.简述
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
2.输入格式
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
3.输出格式
一个整数,代表选点的数目
样例1
Input
2
1 5
4 6
Output
1
样例2
Input
3
1 3
2 5
4 6
Output
2
二、算法
1.主要步骤
第一种贪心的思路,也是网上的思路,比较好写。思路如下:
我们贪心的认为选的点一定是某区间的右端点。我们事先根据右端点对区间进行排序(我这里将区间加入了小根堆)。取出一个区间,初始区间的右端被选定了,下面我们判断这个右端点能往后覆盖多少个区间。我们继续从小根堆中取出一个区间,如果这个区间的左端点
a
a
a要小于等于我们选定的右端点
b
b
b,那么这个区间就被覆盖了。否则
b
b
b就无法继续往后覆盖区间了(可能还可以,但是没必要了。比如第二个区间没法覆盖,有可能第三个区间能覆盖,但是第二个区间迟早要被选定右端点,那么第二个区间就能覆盖第三个区间,所以没必要了),被换为新取出的这个区间的右端点。依次类推,直到小根堆空。
一定要注意是 a ≤ b a\le b a≤b而不是 a < b a\lt b a<b,因为是闭区间,一个点重合也是重合!
下面附上代码:
struct item{//存储区间
int a;
int b;
bool operator<(const item& i)const
{//两个const必须加,这是priority_queue重载的<函数的原因
return b>i.b;//我们想要小根堆
}
item(int a,int b):a(a),b(b){}
bool cover(int aa)
{//传过来某一个区间的左端点,返回是否能覆盖这个区间
return b>=aa;
}
};
while(!q.empty())
{
item i1=q.top();
q.pop();
if(q.empty()) break;
item i2=q.top();
if(i1.cover(i2.a))
{
q.pop();
q.push(i1);
}
else cnt++;
}
//或者(没用到cover函数)
//与上面的稍有区别,思路是完全一样的
int deadline;
if(!q.empty())
{
deadline=q.top().b;
q.pop();
cnt++;
}
while(!q.empty())
{
item i2=q.top();
q.pop();
if(deadline<i2.a)
{
deadline=i2.b;
cnt++;
}
}
我自己写的贪心思路是这样的:
首先提出一个问题,三个区间什么时候能够只取一个点就够了?
假设有
A
:
[
1
,
5
]
A:[1,5]
A:[1,5]、
B
:
[
3
,
8
]
B:[3,8]
B:[3,8]、
C
:
[
4
,
6
]
C:[4,6]
C:[4,6]三个区间,区间
A
A
A和区间
B
B
B合并之后,产生一个重合的小区间
[
3
,
5
]
[3,5]
[3,5],而事实上,只要这个小区间与区间
C
C
C有重合的点或区间,这三个区间就能只取一个点。
我将区间按照左端点的大小排序,将区间压入了小根堆。我一次循环
p
o
p
pop
pop出两个区间,用cover函数(定义在
i
t
e
m
item
item结构体中的)检查第一个区间是否部分覆盖了第二个区间,如果覆盖了,那么必然存在一个重合的小区间
[
a
2
,
b
b
]
[a_2,bb]
[a2,bb],其中
a
2
a_2
a2是第二个区间的左端点,bb=(i1.b<i2.b)? i1.b:i2.b;
即两个区间的右端点中的小的那一个。然后将这个小区间重新压入堆中。如果两个区间并不存在重合的部分,那么就将第二个区间重新压回堆中。这个时候被抛弃的第一个区间就必然要选定一个点了,cnt++
。
具体代码见第三节代码部分。
三、代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
struct item{
int a;
int b;
bool operator<(const item& i)const
{//两个const必须加,这是priority_queue重载的<函数的原因
return a>i.a;//我们想要小根堆
}
item(int a,int b):a(a),b(b){}
bool cover(int aa)
{
return b>=aa;//必须写等于号,我就是在这里错的
}
};
int cnt;
int main()
{
priority_queue<item> q;
int n,i,j;
scanf("%d",&n);
for(i=0;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(a>b) continue;
item t(a,b);
q.push(t);
}
while(!q.empty())
{
item i1=q.top();
q.pop();
if(q.empty()) break;
item i2=q.top();
if(i1.cover(i2.a))
{
q.pop();
int bb=(i1.b<i2.b)? i1.b:i2.b;
q.push(item(i2.a,bb));
}
else cnt++;
}
if(n>0)
cnt++;
printf("%d",cnt);
return 0;
}