题意:给定两个不长于500的字符串,求这两个串的最长公共递增子序列。
思路:首先是一个n^3的算法。dp[i][j]表示考虑s[1...i]和t[1...j],且以t[j]为结尾的最优值,那么状态转移方程为:
1、若s[i]==t[j] , dp[i][j] = max(dp[i-1][k])+1 , 1<=k<j且t[k]<t[j];
2、若s[i]!=t[j] , dp[i][j] = dp[i-1][j];
对于这道题,n^3的做法就能过。接下来注意到第三层循环需要计算的时候仅在s[i]==t[j]之时,那么计算某个dp[i][j]的时候,需要第三层循环的时候t[j]都是等于s[i]的,而s[i]是不变的。所以可以在遍历j的时候把那个最大值求出来。下面是n^2的代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 505
int n,m,now;
int s[N],t[N];
int dp[N][N];
void print(int n,int m){
int k;
if(n>1){
if(dp[n][m] == dp[n-1][m])
print(n-1,m);
else{
for(k = m-1;k>=1;k--)
if(t[k] < t[m]&&dp[n-1][k]==dp[n][m]-1)
break;
print(n-1,k);
printf("%d ",t[m]);
}
}else if(dp[1][m]==1)
printf("%d ",t[m]);
}
int main(){
int i,j,k;
scanf("%d\n",&n);
for(i = 1;i<=n;i++)
scanf("%d",&s[i]);
scanf("%d\n",&m);
for(i = 1;i<=m;i++)
scanf("%d",&t[i]);
clc(dp,0);
for(i = 1;i<=n;i++){
for(j = 1,now = 0;j<=m;j++){
if(s[i] == t[j])
dp[i][j] = now+1;
else
dp[i][j] = dp[i-1][j];
if(t[j] < s[i])
now = max(now,dp[i-1][j]);
}
}
j = 1;
for(i = 2;i<=m;i++)
if(dp[n][i] > dp[n][j])
j = i;
printf("%d\n",dp[n][j]);
print(n,j);
printf("\n");
return 0;
}