The Longest Straight FZU - 2216
二分求解
题意
有n张卡片,每个卡片代表了从1到M的一个数字(可能重复), 这n个卡片中还可能有零,可以用来代表任何从1到M的数字,求最长的连续序列是多少
分析
1 先建立数组, 若在i的位置有一个卡片, 则标记为真,
2 然后用sum数组记录从1到i总共有多少个空缺,
3 再用一个left数组记录每一段已经有卡片的最左边的数字,
4 然后对于每一个left,从left[i] 到 M 二分求最大值, 即求从left[i]开始的最长序列是多少(需要增加一个1,因为可能从1开始)
参考代码
const int LEN = 123456;
int sum[LEN];
int ar[LEN];
int Left[LEN];
int maxlen;
int N,M;
void check(int x,int zero)//从x到M的最长序列是多少
{
if(!ar[x])
zero--;
// if(x+zero>=M)
// {
//maxlen = max(maxlen,M-x+1);
// return ;
// }
int spaces = sum[M]-sum[x];
if(zero>=spaces)////如果从x到M的空缺数量小于0卡片的数量,
//最长序列就是M-x+1
{
maxlen = max(maxlen,M-x+1);
return ;
}
int l = x,r = M;//否则进行二分操作
while(r-l>1)
{
int mid = l+(r-l)/2;
if(sum[mid]-sum[x]==zero)
l = mid;
else if(sum[mid]-sum[x]>zero)
r = mid-1;
else if(sum[mid]-sum[x]<zero)
l = mid+1;
}
if(sum[r]-sum[x]==zero)
maxlen = max(maxlen,r-x+1);
else
maxlen = max(maxlen,l-x+1);
}
int main(void)
{
int T;
cin>>T;
while(T--)
{
maxlen = 0;
me(ar);
me(sum);
me(Left);
cin>>N>>M;
int Zero = 0;
while(N--)
{
int a;
scanf("%d",&a);
if(a==0)
++Zero;
else
ar[a] = 1;
}
ar[0] = 1;
bool sign = false;
int num = 1;
for(int i = 1; i <= M; ++i)
{
sum[i] = sum[i-1] + !ar[i];
if(!sign&&ar[i])
{
Left[num++] = i;
sign = true;
}
if(sign&&!ar[i])
sign=false;
}
check(1,Zero);//可能从1开始
for(int i = 1; i < num; ++i)
{
check(Left[i],Zero);
}
cout<<maxlen<<endl;
}
return 0;
}