题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5773
题目大意:
给一串序列,要你找最长递增子序列(LIS)的长度(要求严格递增),稍有不同的是,其中的0可以转化为任意的整数(包括负数,比赛时没考虑到=-=)。
解题思路:
题目主体还是LIS,就变个型。因为0可以变成任意整数,那么只要先把0全部拿出来,然后对0除外的序列求LIS,最后再加上0的个数即可。对0除外的序列需要进行处理,如果0在原序列之间,显然就不能最后在LIS的长度上直接加0的个数吧。处理方法是用除0之外的序列的每一位减去该位之前的0的个数,以保证结果严格递增。
求LIS选用nlogn的解法:
假设输入的序列为s,保存LIS的序列为a
1、先将s的第一位放在a序列中
2、向后判断s,如果s[i]大于a的每一位(其实只要判断最后一位),则在a的末尾插入s[i];
如果s[i]小于a的最后一位,则用s[i]替换a序列中第一个比它大的数(用二分法找这个数)。
3、重复2的步骤。
代码如下:
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <map>
using namespace std;
const int maxn=100002;
int b[maxn];
int a[maxn];
int n;
int main()
{
int t;
scanf("%d",&t);
int z=1;
while(t--)
{
memset(b,-1,sizeof(b));
cin>>n;
int x=0,num=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==0)
{
num++;
continue;
}
else
a[i] -= num;//每位减去它之前的0的个数
if(a[i]>b[x])
{
x++;
b[x]=a[i];
}
else
{
//二分法找第一个比它大的数
int low=1,high=x,mid;
while ( low <= high )
{
mid = (low + high )/2;
if(a[i]<=b[mid]&&a[i]>b[mid-1])
{
b[mid]=a[i];
break;
}
else if ( b[mid] < a[i])
low = mid + 1;
else if ( b[mid] > a[i] )
high = mid - 1;
}
}
}
cout<<"Case #"<<z<<": ";
cout<<x+num<<endl;
z++;
}
return 0;
}