【大渣】最长上升公共子序列

太坑了所以决定写下来= =

对于两个数列,我们要求他们的最长上升公共子序列该怎么求呢?

用Dp来做,我们很容易想到F[ i ][ j ]来表示A的前i项和B的前j项可以组成的最长上升公共子序列。

并且易得出转移方程:

  a[i]!=b[j]时:F[i][j]=F[i-1][j]

  a[i]==b[j]时:F[i][j]=max(F[i-1][k])+1 (1=<k<j)&&b[j]>b[k]//保证上升

所以我们可以很容易写出一个O(n^3)的代码

       for(i=1;i<=n1;i++)   
       {        
	 for(j=1;j<=n2;j++)    
	 {     
	   if(a[i]!=b[j])f[i][j]=f[i-1][j];     
	   else
	    {
	     for(k=1;k<j;k++)
	      if(b[j]>b[k])f[i][j]=max(f[i][j],f[i-1][k]);//dp	
             f[i][j]+=1;
            } 
	 }   
        }   
         Max=0;   
	 for(i=1;i<=n1;i++) 
	  for(j=1;j<=n2;j++)
	  Max=max(f[i][j],Max);   //取出答案

时间复杂度O(n^3),空间复杂度O(len^2)

但是这样只要数据一大,那么是必T的,所以我们该怎么优化呢0.0

我们分析下,这个Dp是要取max(F[i-1][k]) 那么我们很自然就想到单调队列来优化

  for(i=1;i<=n1;i++)   
   {    
         max=0;    
	 for(j=1;j<=n2;j++)    
	 {     
	 f[i][j]=f[i-1][j];     
	 if(a[i]>b[j]&&max<f[i-1][j])max=f[i-1][j];     
	 if(a[i]==b[j])f[i][j]=max+1;    
	 }   
   }   
	 max=0;   
	 for(i=1;i<=n2;i++) 
	 if(max<f[n1][i])max=f[n1][i];  

来解释下,我们开个Max来维护,i在外围循环,j在内围,所以要求出F[i][1]的前提是先求出F[i-1][1]~F[i-1][n2]的值

而上面的代码,对于a[i]>b[j]时,更新max的值,对于后面的a[i]==b[j]时,这个a[i]是没有改变的,也就是说:a[i]=b[j]>b'[j],那么此时的b[j]一定比前面的b[j]更大

我们就不用拿再一个for循环去求max(F[i-1][k])了!!!(等价于一个单调队列)

时间复杂度O(n^2),空间复杂度O(len^2)

对于上面的代码,我们观察后发现,如果len一大,那么二维数组很可能爆内存,那么有没有什么方法可以优化空间呢?

  for(i=1;i<=n1;i++)   
   {    
     max=0;    
	 for(j=1;j<=n2;j++)    
	 {          
	 if(a[i]>b[j]&&max<f[j])max=f[j];     
	 if(a[i]==b[j])f[j]=max+1;    
	 }   
   }   
    for(j=1;j<=n2;j++)if(ans<f[j])ans=f[j];
    printf("%d\n",ans); 

上面这段代码,我们发现F[]少了一维,却还是能A,因为每次求出F[i][j]后,对于后面的F[i+1][j]并不影响

比如:

F[ ][ ]=1 1 2 2 2  当我们改成一维数组时,i=1时 有F[ ]= 1 1 2 2 2,

          3 3 4 4 4

当i=2时,我们的F[ ]已经赋好了初值了,所以我们相当于在上一次的基础上直接进行操作,得到F[ ]=3 3 4 4 4

所以二维数组就变成了一维的

时间复杂度O(n^2),空间复杂度O(len)

开始的时候怎么都想不到后面两种,多亏果神指点,%%%

相关题目:

http://oi.nks.edu.cn/showproblem?problem_id=1992  【DP】配饰

http://oi.nks.edu.cn/showproblem?problem_id=1035   咒语

点赞