题目大意
给出长度各为n和m的序列a和b,求它们的最长公共上升子序列并输出任一方案。
n,m<=5000
时间限制 1s
空间限制 256M
解题思路
f[i][j]表示a中1~i中的某一位和b中第j位结尾得出的序列长度。
1、a[i]==b[j]:f[i][j]=max(f[i-1][k]),
k<j
&&
b[k]<b[j]
;
2、a[i]!=b[j]:f[i][j]=f[i-1][j];
来看第一条,把转移条件替换一下得出->
k<j
&&
b[k]<a[i]
,
所以我们在DP的时候,在j的那重循环维护满足上述条件的max(f[i-1][j]);
这样转移状态的时间复杂度就是O(1),总的时间复杂度O(nm);
如何输出方案就不再赘述了。
附上代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 5006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,n,m,x,y,tot,ans,a[maxn],b[maxn];
int sum[maxn],sans[maxn],f[maxn][maxn];
int main()
{
scanf("%d",&n);
fr(i,1,n) scanf("%d",&a[i]);
scanf("%d",&m);
fr(i,1,m) scanf("%d",&b[i]);
fr(i,1,n)
fr(j,1,m)
{
f[i][j]=f[i-1][j];
if (a[i]==b[j]) f[i][j]=max(f[i][j],sum[i-1]+1);
if (b[j]<a[i]) sum[i-1]=max(sum[i-1],f[i-1][j]);
ans=max(ans,f[i][j]);
}
printf("%d\n",ans);
x=n,y=m,a[x+1]=1 << 30;
while (ans)
{
bool kan=0;
for(i=x;i;i--)
{
for(j=y;j;j--)
if (a[i]==b[j] && a[i]<a[x+1] && f[i][j]==ans)
{
kan=1;
break;
}
if (kan) break;
}
x=i-1,y=j-1;
sans[++tot]=a[i],--ans;
}
for(i=tot;i;i--) printf("%d ",sans[i]);
printf("\n");
return 0;
}