ZOJ - 2432 / HDU - 1423 最长公共上升子序列

题意:求最长公共上升子序列

LIS:

dp[i] 存 以A[i] 为结尾的最长上升子序列的长度 O(n^2) 普通 O(nlogn) 二分优化

if(A[i] > A[k]) dp[i] = max(dp[i],dp[k]+1) (k < i)

LCS:

dp[i][j] 存以A[i]为结尾和B[j]为结尾的最长公共子序列 O(n^2)

if(A[i] == B[j])

dp[i][j] = dp[i-1][j-1] + 1;

else

dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

综合两个算法 我们很容易可以得到新的状态方程 dp[i][j]表示以A[i]为结尾和以B[j]为结尾的最长公共子序列的长度

if(A[i] == B[j])

dp[i][j] = max(dp[i][j],dp[i][k]+1) (k < j && B[j] > B[k])

else

dp[i][j] = dp[i-1][j]

所以是O(n^3)的复杂度

但是这远远超过了时间限制 所以必须要优化算法

观察可得 dp[i][j] = max(dp[i][j],dp[i][k]+1) (k < j && B[j] > B[k]) 中 若B[j] > B[k] 必有 A[i] > B[k]

所以每次算出的前j项中的最大值 其实是重复计算的 根据这个性质 我们可以优化算法

在第一层循环中 用一个变量MAX 储存 以A[i]为结尾和序列B的最长公共上升子序列的长度 如果A[i] == B[j] 

就更新dp[i][j] = MAX + 1   这样就把时间复杂度降低到了O(n^2)

记录路径可以通过pre[i][j]储存他的前一个数

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[501][501];
long long a[501];
long long b[501];
long long c[501];
int pre[501][501];
int main()
{
    int t;
    cin >> t;
    int n1,n2;
    while(t--) {
        memset(dp,0,sizeof(dp));
        memset(pre,-1,sizeof(pre));
        cin >> n1;
        for(int i = 0; i < n1; i++)
        {
            cin >> a[i];
        }
        cin >> n2;
        for(int i = 0; i < n2; i++)
        {
            cin >> b[i];
        }
        int zan = 1;
        for(int i = 1; i <= n1; i++)
        {
            int MAX = 0;
            zan = 0;
            for(int j = 1; j <= n2; j++)
            {
                dp[i][j] = dp[i-1][j];
                if(a[i-1] > b[j-1])
                {
                    if(MAX < dp[i][j])
                    {
                        zan = j;
                        MAX = dp[i][j];
                    }
                }
                if(a[i-1] == b[j-1])
                {
                    dp[i][j] = MAX + 1;
                    pre[i][j] = zan;
                }
            }
        }
        int ans = 0;
        int f = 0;
        for(int i = 1; i <= n2; i++)
        {
            if(dp[n1][i] > ans)
            {
                ans = dp[n1][i];
                f = i;
            }
        }
        cout<<ans<<endl;
        if(ans)
        {
            int num =0;
            for(int i = n1; i >= 1; i--)
            {
                if(pre[i][f] != -1)
                {
                    c[num++] = b[f-1];
                    f = pre[i][f];
                }
            }
            for(int i = num-1; i>=0; i--)
            {
                if(i==0)
                {
                    cout<<c[i]<<endl;
                }
                else
                {
                    cout<<c[i]<<' ';
                }
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zhengyuan233/article/details/79644739
今日推荐