题目:区间覆盖
题意:数轴上有 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
解题思路:区间覆盖问题,贪心求解;先去头去尾,也就是线段左端点小于1的让他变成1(如果右端点也小于1,并不影响);之后按照线段左端点升序排序。然后每次取右端点最大的(如果左端点相同的话),如果左端点不同,进行一次特判,看下一个点是不是在这个值的左边+1(必须加1,上面有提示),如果在的话,就停止输出-1;不在的话,把要覆盖的线段的左端点变成你当前选的那个线段的右端点,然后继续截断头,直到求出结果为止;
那么这个方法为什么可以呢?我们以覆盖的最左边的那个线段为例,我们选的是左端点小于等于1的线段中右端点的值减去1最大的那个;如果不选这个,那我们只能选比这个更小的,这样你剩下的区间就会变多,你的情况没有变好甚至变坏;同样你选完最左边的之后继续选线段面临的还是这个问题。从这里可以看出我们的贪心应该是最优的。
相似的题目(万恶的奶牛)
坑人之处:请注意上面所说的整点覆盖,本人做的时候还没有这个信息,便以为是线段覆盖,然后疯狂的白给。。。所以一定要加一!
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
bool cmp(pair<int,int> a,pair<int,int> b)
{
return a.first<b.first;
}
int main()
{
int n,t,x,y;
vector<pair<int,int> > h;
while(scanf("%d%d",&n,&t)!=EOF)
{
int total=1,flag=-1,b=1;
for(int i=0;i<n;i++)
{
scanf("%d%d",&x,&y);
if(x>y)//由于被坑的有点厉害,就谨慎了一点
{
swap(x,y);
}
if(x<1){x=1;}//裁剪
if(y>t){y=t;}
h.push_back(make_pair(x,y));
}
if(n==0||t<1)//谨慎
{
printf("-1\n");
h.clear();
continue;
}
sort(h.begin(),h.end(),cmp);//排序
if(h[0].first>1)//特判第一个
{
printf("-1\n");
h.clear();
continue;
}
for(int i=0;i<n;i++)//进行贪心
{
if(h[i].first>b)//如果当前线段左端点数值和b不同,说明左端点为b的线段已经筛选完了
{
if(h[i].first>flag+1)//记住,一定要加个1!!!
{
printf("-1\n");
break;
}
total++;//所选的线段数加1
for(int j=i;j<n;j++)//把左端点进行变更,并进行裁剪
{
if(h[j].first<flag+1)
{
h[j].first=flag+1;
}else
{
break;
}
}
b=flag+1;//变更左端点
}
flag=max(flag,h[i].second);//求个右端点最大的
if(flag==t)//判断一下
{
printf("%d\n",total);
break;
}
if(i==n-1)//扫完了还没成功,就说明失败了
{
printf("-1\n");
}
}
h.clear();
}
}