题意:给你两个数列,叫你求他们的最长公共子序列,并且打印路径
对于最长子序列的n*m算法,网上多的是,可以具体去找一下,这里只做一个小结:
定义f[i][j]表示对于a数组的第i个为止(包括前面的),然后b数组以j结尾(必须是j),的最长公共上升子序列,很容易想到n3复杂的的算法
if( a[ i ] != b[ j ] ) f[ i ][ j ] = f[ i-1 ] [ j ](参考lcs算法)
if( a[ i ] ==b [ j ]) f[ i ][ j ] = max( f[ i-1 ][ k ]+1) k<j
这里的优化就在于每一次都要去求一个max而事实是在遍历这个数字之前,所有的f[i-1][k]的情况都会访问一次,就会有很多重复,所以每一次定义一个之前最大值,用的时候就可以直接更新了,那么接下来的问题就是什么时候更新这个最大值呢?有一个事实是,只有当a[ i ] == b [ j ]的时候我们才会用到这个最大值,所以呢,每次只要 a[ i ]> b[ j ]就更新,保证后面找到的b[ j ]一定比前面大
最后也是最关键的就是打印路径的问题,之前也做过几个类似的打印路径的题,基本上是dp开几维这就几维,但是一开始只想用一维的,而且样例也是没有错的,过了很多组,但是忽略了一个事实就是后面的数字会更改前继。比如这个数据:
6 3 6 9 12 4 6 5 3 4 6 9 12就会错,不懂就自己慢慢调试就知道了
正确代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#define clear(x) memset(x,0,sizeof(x))
#define maxn 521
using namespace std;
int n,m,path[maxn][maxn],f[maxn][maxn],a[maxn],b[maxn],ans,q[maxn];
int main(){
while(~scanf("%d",&n)){
ans=0;
int ai,aj;
clear(f);clear(path);clear(q);
for(int i=1;i<=n;i++)scanf("%d",a+i);
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",b+i);
for(int i=1;i<=n;i++){
int Max=0,mj=0;
for(int j=1;j<=m;j++){
f[i][j]=f[i-1][j];path[i][j]=-1;
if(a[i]>b[j]&&f[i][j]>Max){
Max=f[i][j],mj=j;
}
if(a[i]==b[j]){
f[i][j]=Max+1;
path[i][j]=mj;
}
if(f[i][j]>ans){
ans=f[i][j];
ai=i,aj=j;
}
}
}
printf("%d\n",ans);
int tem=ans;
while(tem){
if(path[ai][aj]!=-1){
q[tem--]=b[aj];
aj=path[ai][aj];
}
ai--;
}
for(int i=1;i<=ans;i++)printf("%d%c",q[i],i==ans?'\n':' ');
}
return 0;
}
bug代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<stack>
#define maxn 1020
using namespace std;
int a[maxn],b[maxn],f[maxn][maxn],n,m,dp[maxn];
stack<int>s;
void front(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(f,0,sizeof(f));
while(!s.empty())s.pop();
}
int main(){
while(scanf("%d",&n)!=EOF){
front();
for(int i=1;i<=n;i++)scanf("%d",a+i);
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",b+i);
for(int i=1;i<=n;i++){
int Max=0;
for(int j=1;j<=m;j++){
f[i][j]=f[i-1][j];
if(a[i]>b[j])Max=max(Max,f[i-1][j]);
if(a[i]==b[j])f[i][j]=max(f[i][j],Max+1);
}
}
int ans=0,j,last=1e9;
for(int i=m;i>=1;i--){
ans=max(ans,f[n][i]);
}
int x=ans;
for(int i=m;i>=1;i--){
if(f[n][i]==x&&b[i]<last){
s.push(b[i]);
x--;last=b[i];
}
}
printf("%d\n",ans);
if(ans==0)printf("\n");
else
while(!s.empty()){
printf("%d ",s.top());
s.pop();
}
printf("\n");
}
return 0;
}/*
4
1 7 6 8
4
1 7 8 6
*/