【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;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",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 }
2.只存每次加入子序列的b[j],不断一个个递减i找到与b[j]匹配的a[i],得到下一个j的pre[i][j]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",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 }