本题属于区间覆盖问题——贪心经典问题之一。
原题可转化为有一个线段[s,t],选择最少的区间将这条线段完全覆盖。不过!!!本题有一个不太一样的就是,上一头牛从t时刻结束,下一头牛可以从t+1时刻开始工作。
贪心策略:
1.先将数据按开始时间a[i]从小到大排序。(a[i]相同可以按b[i]从大到小排序,也可以不用,反正不会影响最终结果)
2.一开始,以s为有效起点,选取结束时间b[i]最长的区间,选完后,有效起点更改为b[i]+1。
3.重复2,知道结束时间>=t。
伪代码:
输入N,T;
输入s[N],t[N];
int flag1=0,flag2=0,sum=1;
long long right,left;
if(s[]中有起点<=1的) flag1=1;
if(t[]中有结束时间>=T的) flag2=1;
if(flag1&&flag2)
{
排序;
right=left=1;
while(i<N&&left<T)
{
if(s[i]小于或等于有效起点right,而且t[i]大于left)left=t[i];
else if(选完了最长区间)
{
if(区间之间“断层”)
{ right=left+1;sum++;left改变}
else break;
}
}
if(left>=T) 输出sum;
else 输出-1
}else 输出-1
代码如下:
#include<cstdio>
#include<cmath>
using namespace std;
long long s[25005],t[25005];
void qsort(int x,int y) //数据较大,应使用快排
{
if(y-x>1) //莫忘
{
int i=x,j=y-1;
long long X=s[x],Y=t[x];
while(i<j)
{
while(i<j&&s[j]>=X)
j--;
if(i<j)
{
s[i]=s[j];
t[i++]=t[j];
}
while(i<j&&s[i]<X)
i++;
if(i<j)
{
s[j]=s[i];
t[j--]=t[i];
}
}s[i]=X;t[i]=Y;
qsort(x,i);
qsort(i+1,y);
}
}
int main()
{
int N,T; //while(1){
int i,flag1=0,flag2=0,sum=1;
long long right,left; //right表有效起点,left表目前覆盖的时间(这里左右弄反了,实际上right和left的作用应反过来,不过也没事啦)
scanf("%d %d",&N,&T);
for(i=0;i<=N-1;i++)
{
scanf("%lld %lld",&s[i],&t[i]);
if(s[i]<=1) flag1=1; //s[]中有有效起点为最开始的 1 的
if(t[i]>=T) flag2=1; //t[]中有最终结束时间为 T 的
}
if(flag1&&flag2)
{
qsort(0,N);
// for(i=0;i<=N-1;i++) printf("%d %d\n",s[i],t[i]);
right=left=1;
i=0;
while(i<N&&left<T) //用while简洁,注意循坏结束条件的控制
{
if(s[i]<=right&&t[i]>left) //选取有效起点为right的最长区间
left=t[i];
else if(s[i]>right) //**“选完了”的标志是:s[i]大于有效起点**
{
if(s[i]<=left+1) //**“不断层”的标志是:s[i]小于或等于left+1**
{
right=left+1;sum++; //更改有效起点
if(t[i]>left) left=t[i]; //如果这时候t[i]大于left,将它的值赋给left
}
else break; //“断层”就可以不用继续了
}
i++;
}
if(left>=T) printf("%d\n",sum);
else printf("-1\n");
}
else printf("-1\n");//}
return 0;
}
代码中要注意两点:
1.选完了的标志;
2.“断层”的标志。
很经典的贪心,要掌握好,紫书中有讲解,要掌握理解好。