题意:求最长上升公共子序列(LCIS),并记录路径.
状态:dp[i][j]表示以s1串的前i个字符s2串的前j个字符且以s2[j]为结尾构成的LCIS的长度。
这里就只给出o(n^2)的算法。
状态:dp[i][j]表示以s1串的前i个字符s2串的前j个字符且以s2[j]为结尾构成的LCIS的长度。
状态转移:当 s1[i-1]!=s2[j-1]时,按照lcs可知由2个状态转移过来,dp[i-1][j],dp[i][j-1],因为dp[i][j]是以s2[j]为结尾构成的LCIS的长度。所以s2[j-1]一定会包含在里面,所以舍去dp[i][j-1],只由dp[i-1][j] 转移过来。当s1[i-1]==s2[j-1],这时肯定要找前面s1[ii-1]==s2[jj-1]的最长且比s2[j-1]小的状态转移过来.
若s1[i-1]!=s2[j-1] 那么dp[i][j]=dp[i][j-1]
若s1[i-1]==s2[j-1] 那么dp[i][j]=MAX{dp[i-1][k]+1(0<k<j),s2[k-1]<s2[j-1]};
这里有一步可以优化就是求上一层的最值,可以在更新的同时就求出来,这样就从o(n^3)优化到o(n^2);时间上就是上10倍的差距。
Run ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
9238676 | 201030720425 | 2127 | Accepted | 2168K | 1000MS | C++ | 1080B | 2011-08-27 00:32:56 |
9238626 | 201030720425 | 2127 | Accepted | 2168K | 79MS | C++ | 989B | 2011-08-27 00:09:30 |
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,rd[505][505],dp[505][505];
int s1[505],s2[505];
void DP()
{
int mac=0,ans=0,I,J,lu[505],mx;
for(int i=1;i<=n;i++)
{
mx=0;
for(int j=1;j<=m;j++)
{
int tem=dp[i][j]=dp[i-1][j];
if(mx<tem&&s1[i-1]>s2[j-1])
{
mx=tem;
mac=j;
}
if(s1[i-1]==s2[j-1])
{
dp[i][j]=mx+1;
rd[i][j]=mac;
}
if(ans<dp[i][j])
{
ans=dp[i][j];
I=i,J=j;
}
}
}
printf("%d\n",ans);
int len=ans;
if(ans>0)
lu[ans--]=J-1;
while(ans&&I&&J)
{
if(rd[I][J]>0)
{
lu[ans--]=rd[I][J]-1;
J=rd[I][J];
}
I--;
}
for(int i=1;i<=len;i++)
{
printf("%d ",s2[lu[i]]);
}
printf("\n");
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&s1[i]);
scanf("%d",&m);
for(int i=0;i<m;i++)
scanf("%d",&s2[i]);
DP();
return 0;
}