最长公共上升子序列的学习

方法1:O($n^2m^2$)

学过dp的都知道。

 

方法2:O($nm\times min(n,m)$)

我们重新定义dp[i][j]:表示a[1…i]和b[2…j]上的LICS, 并且在b中的结束位置为j(重点)。

那么有:

$\begin{cases} &dp[i][j]=dp[i-1][j]\  ,\  a[i] \neq b[j]\\ &dp[i][j]=max(dp[i-1][k])+1(k<j,b[k]<b[j])\ ,\  a[i]=b[j] \end{cases}$

 

方法3:O($nm$)

根据方法2改进。

我们先来看一看方法2的代码:

for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) {

  if(a[i]!=b[j]) dp[i][j]=dp[i-1][j];

  else {

  for(k=1;k<j;++k) if (dp[i][j]<dp[i-1][k] && b[k]<b[j])  dp[i][j] = dp[i-1][k];

  dp[i][j]+=1;

  }

}

这个代码肯定是在循环k的部分优化。准确来说就是把k的那一层循环给搞掉。

具体怎么搞掉呢?

我们知道,当$a[i]=b[j]$时,我们可以将$b[k]<b[j]$换成$b[k]<a[i]$。

我们知道在最外层循环内部,$a[i]$是不变的。所以就可以。。。一边算dp[i][j],一边算$<a[i]$的$b[j]$所对应的最大的LICS(就是dp[i-1][j])。

具体代码:

for(int i=1;i<=n;++i) {
  maxlen=0;f=0;
  for(int j=1;j<=m;++j) {
    dp[i][j]=dp[i-1][j];
    from[i][j]= i>1&&ok(i-1,j)? (i-1)*10000+j : from[i-1][j];
    if(a[i]==b[j]) dp[i][j]=maxlen+1,from[i][j]=f;
    if(b[j]<a[i] && dp[i-1][j]>maxlen) {
    maxlen=dp[i-1][j];
    f= i>1&&ok(i-1,j)? (i-1)*10000+j : from[i-1][j];
    }
  }
}

posted @ 2017-10-09 14:41  shixinyi  阅读(139)  评论(0编辑  收藏  举报