最长不下降子序列问题即是求:一数列中某一严格单增的子序列的最长长度.
举例:6253174中最长不下降的子序列257、237、234.即是最长长度为3.
一、简单的O(N^2)算法
当我们定义问题F(i)为以bi结束的最长不下降序列时,则问题F(I)
有I-1个子问题:F(1), F(2),…, F(I-1)。我们要使F(I)最大,则要找
到一个F(j)(1≤j≤I-1)最大的子问题,且同时满足Bj < Bi,这时F(I)=F(j)max+1;
如果不存在Bj < Bi(1≤j≤I-1),那么i不存在前驱节点,那么F(I)=1;
//用两个数组a[]数组存数num[]数组存这个数对应的最长不下降子序列长度
int longest(int a[],int n)
{
int num[n];
int i,j;
for(i=0;i<n;i++) //每次循环就确定了前i项中的最长不下降子序列的长度并存放在num[i]中.
{
num[i]=1;
for(j=0;j<i;j++)
{
if(a[j]<a[i]&&num[j]+1>num[i]) 找出当前的前去节点 并更新长度.
num[i]=num[j]+1;
}
}
int max=0;
for(i=0;i<n;i++)
if(max<num[i])
max=num[i];
return max;
}
二、复杂的O(NlogN)算法
概述:O(NlogN)的算法在于它建立了一个数组(这里假设为f []).那么f[i]表示读到当前数据的时候 (这个f[]是每读入题目数组的一个元素的时候就更新一次) 长度为i的严格单增序列中结尾元素的最小值,用t来表示当前f[]数组的长度,那么当题目数组全部读完后,t的值就是最长不下降子序列.
具体点来讲:
设当前的已求出来的长度为t,则判断a[i]和b[k];
1.如果a[i]>b[t].即是a[i]大于长度为 t 的序列中的最后一个元素,这样加入a[i]在这个元素可以使当前序列长度加1.t=t+1,然后现在的b[k]=a[i].
2.如果a[i]=b[i],即是a[i]]等于长度为 t 的序列中的最后一个元素,那么这个元素当前有无都不影响.
3.如果a[i]<b[k],那么在b[1]……b[k]中找到最大的 j 使得b[j]<a[i],所以a[i]加入长度为j的j序列里面,这个序列的长度加1,那么就可以更新长度为j+1的序列的最后一个元素,即是b[j+1]=a[i].
#include <stdio.h>
int f[40010];
int longest(int a,int l,int r) //定义二分查找函数
{
int m;
while(l<=r)
{
m=(l+r)/2;
if(f[m]==a) //如果出现当前值和查找的值相同那么自然就更新当前的f[]值,也可理解为不用更新
{
l=m;
return l;
}
else if(f[m]>a)
r=m-1;
else
l=m+1;
}
return l; //每次返回的都是在f[]中最大的j能够使得f[j]<a[i]的j+1.
}
int main()
{
int N,n,a,i,t,k,j;
scanf("%d",&N);
while(N--)
{
t=0;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a); //每次输入一个数就处理一个数
k=longest(a,1,t);
if(k<=t) //如果k<=t说明是情况3.
f[k]=a;
else //否则为情况1,表示最大长度可以更新
{t++;f[t]=a;}
}
printf("%d\n",t);
}
return 0;
}
算法复杂度的分析:
方法一: 因为共有n个元素要进行计算,每次计算又要查找i次(i从1递增到n),那么时间复杂度就是O(N^2).
方法二:由于f[]数组里面的数是单调递增的,那么查找的时候可以不用遍历而用二分查找,这样时间复杂度就为O(NlogN).