您的位置:首页 > 其它

最长单调递增子序列LIS

2015-01-16 11:21 190 查看
问题:

给出一个长度为n的数组,求出最长单调递增子序列(不一定连续,但顺序不能乱)。如 数组 {3 ,1,5,2,7 },结果为 {3,5,7}或{1,5,7},长度为3。

解法 一:最长公共子序列

将原数组按升序排序,然后将得到数组与原数组求最长公共子序列,其结果就是最长递增子序列(以下简称LIS)。

如 3 ,1,5,2,7 排序后得到1,2,3,5,7,其最长公共子序列3,5,7或1,5,7就是所求结果。

解法二:动态规划 o(n^2)

首先找出描述问题的状态。假设要求数组A
的LIS,设d[i]为以A[i]结尾的LIS,那么只要枚举队列中的每一个元素作为序列尾,那么max(d(i),0<=i<N)就是整个数组LIS的长度。那么如何求出每个d(i)呢?以d(i)为状态,那么容易想出它的状态转移方程:d(i)=max(1,d(j)+1),j<i且A[j]<=A[i]。

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>

using namespace std;
const int N=10010;
int n,a
,d
;
int main()
{
    int i,j;
    while(~scanf("%d",&n))
    {
        for (i=0;i<n;d[i]=1, cin>>a[i++]);
        int ans=0;
        for (i=0;i<n;i++){
            for (j=0;j<i;j++){
               if (a[i]>a[j]) d[i]=max(d[i],d[j]+1);
            }
            ans=max(ans,d[i]);
        }
        cout<<ans<<endl;

    }
}


解法三:o(n*lg(n))算法

如何达到n*lg(n)呢?这里的一个n肯定是扫描数组的一重循环,那么在循环内要怎么处理才能是lg(n)呢? 还是假设要求数组A
的LIS,这里再用一个辅助数组d
,d[i]表示长度为i的LIS结尾元素的最小值(因为长度为i的LIS可能有多个,这里只存结尾元素最小的那个),接下来的具体过程如下:

假设A[1...9]={2,1,5,3,6,4,8,9,7 },接着是构造d
数组:

1:将A[1]=2有序插入d
中,此时d
为空,直接放入d[1]的位置,此时d[1]=2,代表以2结尾的序列长度len=1;

2:将A[2]=1,有序插入d
中,因为d[1]=2,所以舍弃2,将1放入d[1]的位置,此时d[1]=1,代表以1结尾的序列长度len=1;

3:将A[3]=5,有序插入d
中,因为d[1]=1<5,而d[1]后没有元素了,所以将5放入d[2]的位置,此时d[2]=5,代表以5结尾的序列长度len=2;

4:接着是插入A[4],因为d[1]<A[4]=3<d[2],所以将3放入d[2]的位置,原来的5就舍弃不要了,此时d[2]=3,代表以3结尾的序列长度len=2;

5:再插入A[5],因为A[5]=6>d[2],所以将6放在d[2]的后面,此时d[3]=6;

6:接着是A[6],由d[2]<A[6]=4<d[3],所以用4覆盖原本的d[3],此时d[3]=4;

7:接着是A[7],由A[7]=8>d[3],放在d[3]的后面,此时d[4]=8;

8:同7,有d[5]=A[8]=9;

9:由于d[3]<A[9]=7<d[4],用7覆盖d[4],此时d[4]=7;

至此,数组A[1...9]已经扫描完了,而数组d[]的长度为4,于是A[1...9]的LIS长度为4。但这里要注意,d[]数组里的序列并不是LIS。

那么为什么d[i]存的要是长度为i的序列中结尾元素最小的那个呢?因为d[i]越小,在i以后就越可能构造出更长的递增序列。

还可以看出,将A[]中元素插入d[]里面时,由于d[]是有序的,因此可以用二分插入,这样时间就降到了 lg(n)!!!

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>

using namespace std;
const int N=10010;
int n,a
,d
;
int binFind(int l,int r, int x)
{
    int i,j,m;
    if (d[r]<x) return r+1;
    while(l<=r){
        m=(l+r)>>1;
        if (d[m]<x) l=m+1;
        else if (d[m]>x) r=m-1;
        else return m;
    }
    return l;
}
int main()
{
    int i,j;
    while(~scanf("%d",&n))
    {
        int t,l=0,r=0;
        cin>>t; d[r]=t;
        for (i=1;i<n;i++){
            cin>>t;
            j=binFind(l,r,t);
            d[j]=t;
            r=max(j,r);
        }
        cout<<r+1<<endl;
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: