题意:
最长公共上升子序列
思路:
以前都是写的n^3的,今天听说有n^2算法,就看了一下。其实就是n^3的优化
以前n^3的方程是:
dp[i][j]表示以a串的前i个数字且以b[j]结尾的的最大的LCIS的长度
转移是:
dp[i][j]=max(dp[i][j],dp[i-1][k]+1) a[i]==b[j]&&b[k]<b[j] 1<=k<j
dp[i][j]=max(dp[i][j],dp[i-1][j]) a[i]!=b[j]
现在开始优化:
第一个方程是n^3的,i,j两层循环是肯定干不掉的,那我们就想办法干掉k的这层循环
可以观察发现循环i层的状态是由i-1层转移过来的,这样就暗示我们要优化循环顺序,i的循环在外部,j的循环在内部
这样我们才可以降低复杂度:
考虑以下不等式:
a[i]==b[j]>b[k] ------> a[i]>b[k]
(仔细想想上面的不等式能给你启发)
那么!这个东西可以再上一层循环中求出来!发现没有?
在进入内层循环时,就可以维护一个最大值ma;
当b[j]<a[i]时,ma=max(ma, dp[i-1][ j])
当出现b[j]==a[i]时,就可以直接取dp[i][j]=ma+1
(说白了,就是把k的循环用j充当了。)
View Code
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <string> 5 #include <iostream> 6 7 #define N 600 8 9 using namespace std; 10 11 int n,m,a[N],b[N],dp[N][N],pre[N][N],stk[N]; 12 13 void go() 14 { 15 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 16 scanf("%d",&m); 17 for(int j=1;j<=m;j++) scanf("%d",&b[j]); 18 memset(dp,0,sizeof dp); 19 dp[0][0]=0; 20 for(int i=1,ma,last;i<=n;i++) 21 { 22 ma=0;last=0; 23 for(int j=1;j<=m;j++) 24 { 25 if(a[i]==b[j]) 26 { 27 dp[i][j]=ma+1; 28 pre[i][j]=last; 29 } 30 else dp[i][j]=max(dp[i][j],dp[i-1][j]); 31 if(b[j]<a[i]&&ma<dp[i-1][j]) 32 { 33 ma=dp[i-1][j]; 34 last=j; 35 } 36 } 37 } 38 int ans=-1,x=n,y,p=0; 39 for(int j=1;j<=n;j++) 40 for(int i=1;i<=m;i++) 41 if(ans<dp[j][i]) 42 { 43 ans=dp[j][i]; 44 y=i; 45 } 46 printf("%d\n",ans); 47 48 while(ans--) 49 { 50 stk[++p]=b[y]; 51 while(a[x]!=b[y]) x--; 52 y=pre[x][y]; 53 x--; 54 } 55 for(int i=p;i>=1;i--) printf("%d ",stk[i]); 56 } 57 58 int main() 59 { 60 while(scanf("%d",&n)!=EOF) go(); 61 return 0; 62 }