【noi 2.6_2000】&【poj 2127】 最长公共子上升序列 (DP+打印路径)

由于noi OJ上没有Special Judge,所以我是没有在这上面AC的。但是在POJ上A了。

题意如标题。

解法:f[i][j]表示a串前i个和b串前j个且包含b[j]的最长公共上升子序列长度

首先,可用3重循环得到,k循环找到b串j之前的最大长度子序列的结尾字符b[k],得以更新现在f[i][j]的状态。
然后,由于k循环的都在j之前,可发现k循环可略去,直接将满足上升的字符用临时变量存,每次j循环的都更新就好了。

具体上,最初f[i][j]=f[i-1][j],先继承好上一个状态,a[i]不加入选出的子序列(平常DP有表示“前...”的初始值都是像这样先可由前一个状态推出的)于是a[i]=b[j]时,就由之前存的最佳答案+1得到。最后由于这次的j是为了下次的a[i]=b[xx]做准备,所以若满足b[j]<a[i]且当前答案最佳,就更新临时储存的临时变量。

另外,打印路径有2种方法。用1维数组存是不行的,因为相同的一个i在和不同的j时,不能直接覆盖掉原来的答案。
1.用2个数组分别记录每次a[i]=b[j]之后ans+1的i和j;

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 const int N=510;
 9 LL a[N],b[N],s[N];
10 int f[N][N],u[N][N],v[N][N];
11 
12 int main()
13 {
14     int n,m;
15     scanf("%d",&n);
16     for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
17     scanf("%d",&m);
18     for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
19     memset(f,0,sizeof(f));
20     memset(u,0,sizeof(u));
21     memset(v,0,sizeof(v));
22     for (int i=1;i<=n;i++)
23     {
24      int p=0,q=0;
25      for (int j=1;j<=m;j++)
26      {
27        f[i][j]=f[i-1][j];
28        u[i][j]=u[i-1][j],v[i][j]=v[i-1][j];
29        if (a[i]==b[j]) f[i][j]=f[p][q]+1,u[i][j]=p,v[i][j]=q;
30        else if (a[i]>b[j] && f[i][j]>f[i][q]) p=i-1,q=j;
31      }
32     }
33     int t=1,cnt=0;
34     for (int j=2;j<=m;j++)
35       if (f[n][j]>f[n][t]) t=j;
36     printf("%d
",f[n][t]);
37     s[++cnt]=b[t];
38     int x=n,y=t,xx,yy;
39     while (f[x][y])
40       s[++cnt]=b[y],xx=x,yy=y,x=u[xx][yy],y=v[xx][yy];
41     for (int i=cnt;i>=1;i--) printf("%lld ",s[i]);
42     return 0;
43 }
View Code 1

2.只存每次加入子序列的b[j],不断一个个递减i找到与b[j]匹配的a[i],得到下一个j的pre[i][j]。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 const int N=510;
 9 LL a[N],b[N],s[N];
10 int f[N][N],pre[N][N];
11 
12 int main()
13 {
14     int n,m;
15     scanf("%d",&n);
16     for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
17     scanf("%d",&m);
18     for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
19     memset(f,0,sizeof(f));
20     memset(pre,0,sizeof(pre));
21     for (int i=1;i<=n;i++)
22     {
23      int p=0;
24      for (int j=1;j<=m;j++)
25      {
26        f[i][j]=f[i-1][j];
27        if (a[i]==b[j]) f[i][j]=f[i][p]+1,pre[i][j]=p;
28        else if (a[i]>b[j] && f[i-1][j]>f[i-1][p]) p=j;
29      }
30     }
31     int t=1,cnt=0;
32     for (int j=2;j<=m;j++)
33       if (f[n][j]>f[n][t]) t=j;
34     printf("%d
",f[n][t]);
35     int x=n,y=t;
36     while (f[x][y])
37     {
38       while(a[x]!=b[y]&&x) x--;
39       s[++cnt]=b[y];
40       y=pre[x][y];
41     }
42     for (int i=cnt;i>=1;i--) printf("%lld ",s[i]);
43     return 0;
44 }
View Code 2
原文地址:https://www.cnblogs.com/konjak/p/5958404.html