C-区间覆盖(不支持C++11)
一、题目描述
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1
输入
第一行:N和T
第二行至N+1行: 每一行一个闭区间。
输出
选择的区间的数目,不可能办到输出-1
样例输入
3 10
1 7
3 6
6 10
样例输出
2
提示
这道题输入数据很多,请用scanf而不是cin
二、思路与算法
要求出能覆盖某一段区间的最少区间数,本题主要算法是,先将所有区间排序(以区间左端点为第一关键字升序,以区间右端点为第二关键字升序),再把[1,t]两边的区间切掉。
之后,最核心的为:每选定一个区间,将已经覆盖的部分都切掉,在能继续向下覆盖的区间中,选定最大的区间,直到[1,t]都被覆盖完成。
要注意的地方是:1.只要求整数覆盖,所以有+1的要求。
2.选定的第一个区间的起点必须是1,否则判为不可能实现。
3.重点可以超出t,例如[1,12]覆盖[1,10]
三、代码实现
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct period{
int a,b;
period(){}
period(int aa,int bb){a=aa; b=bb;}
bool operator <(const period &p)const{ //重载<
if(a!=p.a){return a<p.a;}
else{return b<p.b;}
}
};
int main(){
int N,T;
vector<period> all(0);
int ta,tb;
//读入所有区间
scanf("%d %d",&N,&T);
for(int i=0;i<N;i++){
scanf("%d %d",&ta,&tb);
all.push_back(period(ta,tb));
}
sort(all.begin(),all.end());
int s=all.size();
//切掉[1,T]以外的部分
while(all.empty()==0&&all[s-1].a>T){
all.pop_back();
s--;
}
while(all.empty()==0&&all[0].a<1){
vector<period>::iterator it=all.begin();
all.erase(it);
s--;
}
//开始选择区间
if(all.empty()==1){printf("-1\n"); return 0;}
int result=0; //需要的区间总数
if(all[0].a>1){printf("-1\n"); return 0;}
int tmp=0,j=0;
int latest=1,flag=0;
while(latest<T)
{
int maxlen=0; //目前长度最长区间长度
if(flag==0){ //flag只在第一次为0,处理第一个区间
flag=1;
for(int i=0;(all[i].a==1)&&i<all.size();i++){
tmp=all[i].b-1;
if(maxlen<tmp){maxlen=tmp; j=i;}
}
if(maxlen==0) {printf("-1\n"); return 0;}
result++; latest=all[j].b;
}
else{
for(int i=0;(i<all.size())&&(all[i].a<=(latest+1));i++)//找当前左边界最长
{ if(all[i].b>latest){
tmp=all[i].b-latest;
if(tmp>maxlen)
{
maxlen=tmp;
j=i;
}
}
}
if(maxlen==0) {printf("-1\n"); return 0;}//不存在覆盖
else{
result++;
latest=all[j].b;
}
}
}
printf("%d\n",result);
return 0;
}
四、经验与总结
- 切记切记!!重载<时,一定要包含所有情况!当不相等时返回什么,相等时又返回什么,不然会WA!
- 不确定循环边界等数据的时候,可以用数据测试,跟踪数据运行情况确定边界设定是否正确,错的话该如何改。
- scanf时间性能比cin好,更推荐用scanf、printf。
- 贪心指标是贪心算法最重要的部分,贪心算法的正确性依赖指标的正确性,所以贪心指标一定要慎重选择!