题意:
数轴上有n(1<=n<=25000)个闭区间[ai,bi],选择尽量少的区间覆盖一条指定线段[1,T] (1<=T<=1000000)。覆盖整点,即[1,2]+[3,4]可以覆盖[1,4]。不可能办到输出-1。输入第一行为n和T,之后N行每行一个闭区间。输出选择区间的数目,不可能办到输出-1。
思路:
贪心准则:从已覆盖的区间段中选出一个a,它有最大的b,然后删除最大b之前的区间。
首先读入时如果区间在[1,T]之外,直接不记录。读完所有区间后应判断是否没有在[1,T]之间的区间。
之后对所有区间按a从小到大,b从大到小排序。
按照贪心准则,一开始没选区间时,从a<=1的区间中找到最大b。如果没有a<=1且b>1的区间,输出-1。
此时已经新覆盖了[lasta,lastb],需要从[lasta,lastb+1]中寻找一个a,它有最大b。找到后应更改lasta与lastb。
注意到最后一个区间时,应判断是否选了新区间。
总结:
一道贪心题,贪心准则好想,但实现起来有些困难。如果简单的找到最大b后删除b之前的区间会TLE。所以直接遍历数组,当找到最大b时,数组索引之前的区间就是要删除的区间,不用删除。
同时应注意不能覆盖的各种情况,少判断一种都会WA。
代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct qujian
{
int a,b;
qujian(int a1=0,int b1=0)
{
a=a1,b=b1;
}
};
bool cmp(const qujian&q1,const qujian&q2)
{
if(q1.a<q2.a) return true;
else if(q1.a>q2.a) return false;
else
{
if(q1.b>q2.b) return true;
else return false;
}
}
int main()
{
std::ios::sync_with_stdio(false);
int N,T;
cin>>N>>T;
vector<qujian> v;
v.reserve(25000); //使vector容量扩充,避免多次扩充操作,也防止MLE
for(int i=0; i<N; i++)
{
int a,b;
cin>>a>>b;
if(b<1||a>T) continue; //砍掉[1,T]之外的区间
qujian q(a,b);
v.push_back(q);
}
if(v.empty()) //没有在[1,T]的区间
{
cout<<-1<<endl;
return 0;
}
int ans=0;
sort(v.begin(),v.end(),cmp); //按a从小到大排序
//从a<=1的区间找到最大b
int index=0; //vector下标
int maxb=1;
for(;index<v.size();index++)
{
if(v[index].a<=1&&v[index].b>maxb)
maxb=v[index].b;
else if(v[index].a>1)
break;
}
ans++; //覆盖了[1,maxb]
if(maxb==1) //没有a<=1&&b>1的区间
{
cout<<-1<<endl;
return 0;
}
int lasta=1,lastb=maxb;
//此时已经新覆盖了[lasta,lastb],需要从[lasta,lastb+1]中寻找一个a,它有maxb
for(;index<v.size();index++)
{
//此时已经覆盖了[lasta,lastb],判断是否已经覆盖[1,T]
if(lastb>=T) break;
if(v[index].a>=lasta&&v[index].a<=lastb+1&&v[index].b>maxb)
maxb=v[index].b;
else if(v[index].a>lastb+1) //选取了新区间,应更改lasta和lastb
{
if(maxb>lastb) //选了新区间
{
lasta=lastb+1,lastb=maxb;
ans++;
index--;
continue;
}
else break;
}
if(index==v.size()-1) //最后一个,判断是否选了新区间
{
if(maxb>lastb) //选了
{
lasta=lastb,lastb=maxb;
ans++;
}
//没选,什么也不做(此时覆盖不了[1,T])
}
}
//判断是否覆盖了[1,T]
if(lastb>=T) cout<<ans<<endl;
else cout<<-1<<endl;
}