POJ 2127 Greatest Common Increasing Subsequence【最长公共递增子序列】
题意: 给两个数组,求最长公共递增子序列。
分析: dp[i,j]表示a串前i个字符,b串前j个字符组成的,并且以b[j]为结尾的最长的LCIS,
转移方程:
dp[i,j]=dp[i-1,j]; //a[i]与b[j]不等
dp[i,j]=dp[i-1,k]+1; (1<=k<=j-1) //a[i]与b[j] 相等
以上转移方程是O(n^3)时间复杂度
优化:
由于最外层循环是 i,第二层是 j,循环 j 的时候,实际上同时找出dp[i-1,k] 的最大值MAX
方法:循环 j 的同时,若a[i]>b[j],更新MAX (因为当且仅当a[i]>b[j]时,后边循环 j 时可能用到这个决策来转移)
若a[i] = b[j] 则用MAX+1更新 dp[i,j],记录路径。
#include<stdio.h> #include<string.h> #define maxn 505 #define clr(x)memset(x,0,sizeof(x)) int a[maxn]; int b[maxn]; int dp[maxn][maxn]; int path[maxn][maxn]; int ans[maxn]; int main() { int l1,l2,i,j,ai,aj,mx,mj,res,tmp; while(scanf("%d",&l1)!=EOF) { res=0; clr(dp); clr(path); for(i=1;i<=l1;i++) scanf("%d",&a[i]); scanf("%d",&l2); for(i=1;i<=l2;i++) scanf("%d",&b[i]); for(i=1;i<=l1;i++) { mx=0; for(j=1;j<=l2;j++) { dp[i][j]=dp[i-1][j]; path[i][j]=-1; if(b[j]<a[i]&&dp[i-1][j]>mx) { mx=dp[i-1][j]; mj=j; } else if(a[i]==b[j]) { dp[i][j]=mx+1; path[i][j]=mj; } if(res<dp[i][j]) { res=dp[i][j]; ai=i; aj=j; } } } printf("%d\n",res); tmp=res; while(tmp) { if(path[ai][aj]>-1) { ans[tmp--]=b[aj]; aj=path[ai][aj]; } ai--; } for(i=1;i<=res;i++) printf("%d%c",ans[i],i==res?'\n':' '); } return 0; }