您的位置:首页 > 其它

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

2018-03-21 20:57 375 查看
题意:求最长公共上升子序列
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]<<' ';
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: