https://www.acwing.com/problem/content/274/
题意
给定两个序列数列 A A A, B B B,求它们的最长公共上升子序列。
公共上升子序列的定义是可在 A A A, B B B元素集合交集中保序选取一部分构成新的序列,该序列是严格递增的。
思路
考虑最长上升子序列(LIS)和最长公共子序列(LCS)的转移方程和定义
- LIS
f
[
i
]
=
m
a
x
0
≤
j
<
i
,
A
[
j
]
<
A
[
i
]
f
[
j
]
+
1
f[i]=max_{0 \leq j <i,A[j]<A[i]}{f[j]+1}
f[i]=max0≤j<i,A[j]<A[i]f[j]+1,f[i]代表
A[1]~A[i]
构成的LIS长度 - LCS:如
A
[
i
]
!
=
B
[
j
]
A[i]!=B[j]
A[i]!=B[j],则
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
i
−
1
]
)
f[i][j]=max(f[i-1][j],f[i][i-1])
f[i][j]=max(f[i−1][j],f[i][i−1]),否则
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
−
1
]
+
1
)
f[i][j]=max(f[i-1][j],f[i][j-1],f[i-1][j-1]+1)
f[i][j]=max(f[i−1][j],f[i][j−1],f[i−1][j−1]+1),
f[i][j]
代表A[1]~A[i]
和B[1]~B[i]
构成的LCS的长度
我们可以如此定义最长公共上升子序列(LCIS)的转移方程
当
A
[
i
]
!
=
B
[
j
]
A[i]!=B[j]
A[i]!=B[j]时
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j]
否则
f [ i ] [ j ] = m a x 0 ≤ k < j , B [ k ] < B [ j ] f [ i − 1 ] [ k ] + 1 = m a x 0 ≤ k < j , B [ k ] < A [ i ] f [ i − 1 ] [ k ] + 1 \begin{align} f[i][j]&= max_{0 \leq k < j,B[k]<B[j]}{f[i-1][k]+1}\\ &=max_{0 \leq k < j,B[k]<A[i]}{f[i-1][k]+1} \end{align} f[i][j]=max0≤k<j,B[k]<B[j]f[i−1][k]+1=max0≤k<j,B[k]<A[i]f[i−1][k]+1
其中 f[i][j]
表示A[1]~A[i]
和B[1]~B[i]
构成的LCIS的长度
这可由一个三重循环来实现
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j]){
for(int k=0;k<j;k++){
if(b[k]<a[i]){
f[i][j]=max(f[i][j],f[i-1][k]+1);
}
}
}
else f[i][j]=f[i-1][j];//a[i]!=b[j]
}
}
注意到if(b[k]==a[i])
在j
循环的内部,在j
从1
到m
变化的过程中a[i]
都是不变的,因此实际上在每一次j
的循环和自增中,我们只需要单独考虑k所增加的一个新情况,即k=j-1
的情况即可。
由此,我们可以将
for(int k=0;k<j;k++)
if(b[k]<a[i])
f[i][j]=max(f[i][j],f[i-1][k]+1);
这一部分做出优化,提取到j
循环的外部,得到
O
(
n
2
)
O(n^2)
O(n2)的实现
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
int n, m;
const int N = 3010;
int a[N], b[N];
int f[N][N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
for (int i = 1; i <= n; i++) {
int maxv = 1;
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j];
if (a[i] == b[j])
f[i][j] = max(f[i][j], maxv);
if (a[i] > b[j])
maxv = max(maxv, f[i - 1][j] + 1);
}
}
int res = 0;
for (int i = 1; i <= n; i++)
res = max(res, f[n][i]);
cout << res << endl;
return 0;
}