1. 程式人生 > >最長單調遞增子序列的三種解法

最長單調遞增子序列的三種解法

問題描述:

找出由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;
}