题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5748
题意:就是求以各个数字为结尾的最长递增序列(包括本身),输出每个数字为结尾的最长递增序列的长度
方法:M[i]表示序列长度为i的结尾最小的值,d[i]保存已第i为结尾的最长递增序列的长度,从第一个数开始往后判断,每次维护时先判断是否大于当前最长递增子序列的末尾值,若大于则扩充M的长度len,若不大于就通过二分法查找M[i]中恰好比此时值小的最大值<M[i+1]比此时值大>,返回i+1(可以建立以M[i]为上一个值得序列),更新M[i+1]和d[i],此方法可以的理由是每次都保证了M[i]是以i为结尾序列的最小值
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int value[100050];
int M[100050];
int d[100050];
int findTwo(int v,int p)
{
int low=1,high=p;
while(low<=high)//注意一定要有等于号 保证返回的是low比high大一的情况
{
int mid=(low+high)/2;
if(v>M[mid])
{
low=mid+1;
}
else if(v<M[mid])
{
high=mid-1;
}
else
{
return mid;
}
}
return low;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&value[i]);
}
d[1]=1;
M[1]=value[1];
int len=1;
for(int i=2;i<=n;i++)
{
if(value[i]>M[len])
{
M[++len]=value[i];
d[i]=len;
}
else
{
int pt=findTwo(value[i],len);
M[pt]=value[i];
d[i]=pt;
}
}
for(int i=1;i<n;i++)
{
printf("%d ",d[i]);
}
printf("%d\n",d[n]);
}
return 0;
}