POJ 2127 Greatest Common Increasing Subsequence【最长公共递增子序列】

题意: 给两个数组,求最长公共递增子序列。

分析: dp[i,j]表示a串前i个字符,b串前j个字符组成的,并且以b[j]为结尾的最长的LCIS,

         转移方程:
         dp[i,j]=dp[i-1,j];        //a[i]与b[j]不等
         dp[i,j]=dp[i-1,k]+1;   (1<=k<=j-1) //a[i]与b[j] 相等
         以上转移方程是O(n^3)时间复杂度

   优化:
        由于最外层循环是 i,第二层是 j,循环 j 的时候,实际上同时找出dp[i-1,k] 的最大值MAX

        方法:循环 j 的同时,若a[i]>b[j],更新MAX (因为当且仅当a[i]>b[j]时,后边循环 j 时可能用到这个决策来转移)

                若a[i] = b[j] 则用MAX+1更新 dp[i,j],记录路径。

#include<stdio.h>
#include<string.h>
#define maxn 505
#define clr(x)memset(x,0,sizeof(x))
int a[maxn];
int b[maxn];
int dp[maxn][maxn];
int path[maxn][maxn];
int ans[maxn];
int main()
{
    int l1,l2,i,j,ai,aj,mx,mj,res,tmp;
    while(scanf("%d",&l1)!=EOF)
    {
        res=0;
        clr(dp);
        clr(path);
        for(i=1;i<=l1;i++)
            scanf("%d",&a[i]);
        scanf("%d",&l2);
        for(i=1;i<=l2;i++)
            scanf("%d",&b[i]);
        for(i=1;i<=l1;i++)
        {
            mx=0;
            for(j=1;j<=l2;j++)
            {
                dp[i][j]=dp[i-1][j];
                path[i][j]=-1;
                if(b[j]<a[i]&&dp[i-1][j]>mx)
                {
                    mx=dp[i-1][j];
                    mj=j;
                }
                else if(a[i]==b[j])
                {
                    dp[i][j]=mx+1;
                    path[i][j]=mj;
                }
                if(res<dp[i][j])
                {
                    res=dp[i][j];
                    ai=i;
                    aj=j;
                }
            }
        }
        printf("%d\n",res);
        tmp=res;
        while(tmp)
        {
            if(path[ai][aj]>-1)
            {
                ans[tmp--]=b[aj];
                aj=path[ai][aj];
            }
            ai--;
        }
        for(i=1;i<=res;i++)
            printf("%d%c",ans[i],i==res?'\n':' ');
        
    }
    return 0;
}

 

posted @ 2012-08-25 09:06  'wind  阅读(1362)  评论(0编辑  收藏  举报