最长单调递增子序列的三种解法

来源:互联网 发布:mysql创建数据库 编辑:程序博客网 时间:2024/06/05 02:08
问题描述:

找出由n个数组成的序列的最长单调递增子序列

解法一:转化成LCS问题求解,时间复杂度为O(n*n).

思路:原序列为A,把A按升序排序得到序列B,求出A,B序列的最长公共子序列,即为A的最长单调递增子序列。

#include<iostream>#include<algorithm>#include<string>#include<cstdio>using namespace std;//转化成LCS问题,时间复杂度O(n*n)int d[105][105];int a[105];int b[105];int c[105][105];void LCS_path(int i,int j)   //打印路径{    if(i==0||j==0) return;    if(c[i][j]==1)    {        LCS_path(i-1,j-1);        cout<<a[i]<<" ";  //a[i]==b[j]    }    else if(c[i][j]==2)    {        LCS_path(i-1,j);    }    else    {        LCS_path(i,j-1);    }}int main(){    int i,j,n,m;    //freopen("d:\\test.txt","r",stdin);    cin>>m;    while(m--)    {        cin>>n;        for(i=1; i<=n; i++)        {            cin>>a[i];            b[i]=a[i];        }        sort(b+1,b+n+1);        for(i=1; i<=n; i++)        {            for(j=1; j<=n; j++)            {                if(a[i]==b[j])                {                    d[i][j]=1+d[i-1][j-1];                    c[i][j]=1;                }                else if(d[i-1][j]>d[i][j-1])                {                    d[i][j]=d[i-1][j];                    c[i][j]=2;                }                else                {                    d[i][j]=d[i][j-1];                    c[i][j]=3;                }            }        }        LCS_path(n,n);        cout<<endl;        cout<<d[n][n]<<endl;    }    //fclose(stdin);    return 0;}

解法二:设d[i]为以第i个元素结尾的最长递增子序列的长度,则d[i]=max{0,d[j] | j<i,a[j]<a[i]}+1,ans=max{d[i]}.   时间复杂度O(n*n)

#include<iostream>#include<algorithm>#include<cstdio>using namespace std;int d[105];int a[105];int main(){    int i,j,n,m;    //freopen("d:\\test.txt","r",stdin);    cin>>m;    while(m--)    {        cin>>n;        for(i=0; i<n; i++)        {            cin>>a[i];        }        d[0]=1;        int ans=0;        for(i=1;i<n;i++)        {            int Max=0;            for(j=i-1;j>=0;j--)            {                if(a[i]>a[j])                {                    if(Max<d[j]) Max=d[j];                }            }            d[i]=Max+1;            ans=max(ans,d[i]);        }        cout<<ans<<endl;    }    //fclose(stdin);    return 0;}

解法三:设d[i]为以第i个元素结尾的最长递增子序列的长度。假设已经计算出的两个状态p和q满足a[p]<a[q],且d[p]=d[q],对于后续所有状态(i>p且i>q)来说,p一定比q好。所以此时只保留p一定不会丢失最优解。所以对于相同的d值,只需保留a[i]最小的一个。g[i]表示d值为i的最小状态编号(g[i]初始化为正无穷)。

在给定状态 i 时,可用二分查找找到满足g[k]>=a[i]的第一个下标k,d[i]=k,此时a[i]<g[k],而d[i]=k,所以更新g[k]=a[i].        时间复杂度O(nlogn).

#include<iostream>#include<algorithm>#include<cstdio>using namespace std;const int INF=1e9;int d[105];int a[105];int g[105];int main(){    int i,j,n,m;    //freopen("d:\\test.txt","r",stdin);    cin>>m;    while(m--)    {        cin>>n;        for(i=0; i<n; i++)        {            cin>>a[i];        }        int ans=0;        for(i=1;i<=n;i++) g[i]=INF; //初始化g[i]        for(i=0;i<n;i++)        {            int k=lower_bound(g+1,g+1+n,a[i])-g;            d[i]=k;            g[k]=a[i];        //更新g[k],使g数组保持最小递增序列(第1~n个元素均为当前可取最小值),不会丢失最优解            ans=max(d[i],ans);        }        cout<<ans<<endl;    }    //fclose(stdin);    return 0;}



2 0
原创粉丝点击