主要思路:
一道贪心的简单应用
将所有的区间进行排序(先按右端点升序;右端点相同按左端点降序)
选点的原则是:
- 将(排序后)第一个区间抽出
- 选择该区间的右端点
- 将包含该区间的右端点的所有区间删去
重复上述操作直至处理完所有的区间
简短的证明
要想使得每个区间上都有点,且只能有一个,使用上述由右至左选区间的策略:如若我们选择最左端点优先的策略进行,那么面临形如(1,4)和 (2,3)的区间时,按左端点升序则会选择1号点,那么(2,3)就无法包括;这只是其中一种造成非最优解的情况。
并且这里选点的策略是取尽量靠近右边界的点,这样选取被满足区间个数会是最大的。
坑
最初我使用了vector的数据结构,每个元素为自己定义的结构体interval。整体思路也没有什么问题,然而连样例都无法通过。在仔细调试后,将问题定位至删除操作的地方。我使用的vector里的erase方法,即传入指向待删除元素的迭代器。
当时vector中仅剩两个元素(区间),删去一个后,会发现之前被删的元素又会重新出现。在仔细阅读stl的文档后,发现还是因为我没有读懂erase方法的功能。
在删除单个的元素后,迭代器指向不会发生变化,仍然指向已经删除的元素,而被删除的元素之后的元素全部向前移动“一格“,这样一来,迭代器指向的是原来被删除元素的下一个元素。至此,bug解决。
具体错误示范:
//想要删掉元素4
vector<int> v{1,2,3,4,5};
for(auto i = v.begin();i != end();i++){
if(*i==4)
v.erase(i); //在将4删去后,4之后的整体想起移动一格
//那么i指向了元素5
//此时再进行i++的操作就是不合法的
//这玩意就叫野指针
}
正确示范:
//想要删掉元素4
vector<int> v{1,2,3,4,5};
for(auto i = v.begin();i != end();){
if(*i==4)
v.erase(i); //即进行了删除操作,就不进行i++
else
i++;
}
B - 区间选点
数轴上有 n 个闭区间 [a,b]。
取尽量少的点,使得每个区间内都至少有一个点
不同区间内含的点可以是同一个
Input
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
Output
一个整数,代表选点的数目
Sample Input
2
1 5
4 6
Sample Output
1
- A Possible Solution
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
typedef struct tag_interval {
int l, r;
tag_interval(int L, int R) {
l = L;
r = R;
}
bool operator < (const tag_interval& x) {
if (r == x.r)
return l < x.l;
else
return r > x.r;
}
}interval;
int main() {
int n;
vector<interval> v;
vector<int> path;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int l, r;
scanf("%d %d", &l, &r);
interval tmp(l, r);
v.push_back(tmp);
}
sort(v.begin(),v.end());
while(!v.empty()){
interval tmp=v.back();
int p=tmp.r;
v.pop_back();
path.push_back(p);
for(auto i=v.begin();(!v.empty()) && (i!=v.end());){
if(p>=i->l && p<=i->r){
v.erase(i);
}
else{
i++;
}
}
}
printf("%d\n",path.size());
return 0;
}