最长公共上升子序列的学习
方法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];
}
}
}