最长上升子序列LIS

问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7....an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质,s1<s2<s3<...<sn并且这个子序列的长度最长。输出这个最长的长度。(为了简化该类问题,我们将诸如最长下降子序列及最长不上升子序列等问题都看成同一个问题,其实仔细思考就会发现,这其实只是<符号定义上的问题,并不影响问题的实质)


例如有一个序列:1  7  3  5  9  4  8,它的最长上升子序列就是 1 3 4 8 长度为4.

算法1:动态规划:时间复杂度:(n^2):我们依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列 i 的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[0]=1,我们从i=1开始遍历后面的元素即可。

下面是模板:

int lis(int a[],int n)
{
    int i,j,ans=1,m=0,*dp=new int[n+1];
    dp[0]=1;
    for(i=1;i<n;i++)
    {
        m=0;
        for(j=0;j<i;j++)   //可以改进一下再求出这个子序列的第一和最后一个元素
            if(dp[j]>m&&a[j]<a[i]) m=dp[j];
        dp[i]=m+1;
        if(dp[i]>ans) ans=dp[i];//1  7  3  5  9  4  8
    }
    return ans;
}

算法2:二分法:时间复杂度:(nlog(n))维护一个一维数组c,并且这个数组是动态扩展的,初始大小为1,c[i]表示最长上升子序列长度是i的所有子串中末尾最小的那个数,根据这个数字,我们可以比较知道,只要当前考察的这个数比c[i]大,那么当前这个数一定能通过c[i]构成一个长度为i+1的上升子序列。当然我们希望在C数组中找一个尽量靠后的数字,这样我们得到的上升子串的长度最长,查找的时候使用二分搜索,这样时间复杂度便下降了。
模板如下:

int a[1000],c[1000],len;
int bsearch(int l,int r,int x)
{
    if(l==r) return l;
    int p,mid=(l+r)/2;
    if(c[mid]<x) return p=bsearch(mid+1,len,x);
    else return p=bsearch(l,mid,x);
}
int lis(int n)
{
    int i,j;
    len=0;
    c[0]=-100000000;
    for(i=0; i<n; i++)
    {
        if(a[i]>c[len]) j=++len;
        else j=bsearch(1,len,a[i]);
        c[j]=a[i];
    }
    return len;
}


你可能感兴趣的:(最长上升子序列LIS)