题目链接:https://www.acwing.com/problem/content/description/274/
最长公共上升子序列
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。
小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。
不过,只要告诉奶牛它的长度就可以了。
数列A和B的长度均不超过3000。
输入格式
第一行包含一个整数N,表示数列A,B的长度。
第二行包含N个整数,表示数列A。
第三行包含N个整数,表示数列B。
输出格式
输出一个整数,表示最长公共子序列的长度。
数据范围
1≤N≤3000,序列中的数字均不超过 2 31 − 1 2^{31}−1 231−1
输入样例:
4
2 2 1 3
2 1 2 3
输出样例:
2
题解
LCS
d p [ i ] [ j ] = dp[i][j]= dp[i][j]= a 1 a_1 a1… a i a_i ai和 b 1 b_1 b1… b j b_j bj对应的LCS的长度
d p [ 0 ] [ j ] = 0 dp[0][j]=0 dp[0][j]=0
d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + 1 ( a i = b j ) m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) (其他) dp[i][j]= \begin{cases} dp[i-1][j-1]+1& \text{($a_{i}=b_{j})$}\\ max(dp[i-1][j],dp[i][j-1])& \text{(其他)} \end{cases} dp[i][j]={dp[i−1][j−1]+1max(dp[i−1][j],dp[i][j−1])(ai=bj)(其他)
LIS
d p [ i ] = dp[i]= dp[i]=以 a i a_i ai(即arr[i])为末尾的最长上升子序列的长度
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ∣ j < i 且 a j < a i ) dp[i]=max(dp[i],dp[j]+1|j<i且a_j<a_i) dp[i]=max(dp[i],dp[j]+1∣j<i且aj<ai)
LCIS
我们想办法糅合这两种动态规划的思想,设 f [ i ] [ j ] f[i][j] f[i][j]代表的是 a a a序列前i个元素与 b b b序列前j个元素并且以 a j a_j aj 结尾的LCIS长度(最长公共上升子序列), t t t为最长 L C I S LCIS LCIS的结尾元素在b位置,我们可以推出状态转移方程
d p [ i ] [ j ] = { m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ t ] + 1 ) ( a i = b j ) d p [ i − 1 ] [ j ] (其他) dp[i][j]= \begin{cases} max(dp[i-1][j],dp[i-1][t]+1)& \text{($a_{i}=b_{j}$)}\\ dp[i-1][j]& \text{(其他)} \end{cases} dp[i][j]={max(dp[i−1][j],dp[i−1][t]+1)dp[i−1][j](ai=bj)(其他)
优化
分析状态转移方程可知
f
[
i
]
[
j
]
f[i][j]
f[i][j]都是由
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]得来的,因此可以优化空间,同背包问题的优化类似,将二维数组优化成一维。新的状态转移方程为
f
[
j
]
=
{
f
[
t
]
+
1
(
a
i
=
b
j
)
f
[
j
]
(其他)
f[j]= \begin{cases} f[t]+1& \text{($a_{i}=b_{j})$}\\ f[j]& \text{(其他)} \end{cases}
f[j]={f[t]+1f[j](ai=bj)(其他)
这样空间就降到了一维
代码
二维做法
#include<bits/stdc++.h>
using namespace std;
const int N = 3005;
int a[N], b[N];
int f[N][N];
int n;
int main(void)
{
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 = 0; // 前边最多有多少个满足b[j]<a[i]的
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 + 1); // 更新答案
if (b[j] < a[i])
maxv = max(maxv, f[i][j]); // 更新maxv
}
}
int res = 0;
for (int i = 1; i <= n; i++)
res = max(res, f[n][i]);
cout<<res<<endl;
}
一维优化
#include<bits/stdc++.h>
using namespace std;
const int N = 3005;
int a[N], b[N];
int f[N];
int n;
int main(void)
{
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 = 0; //前边最多有多少个满足b[j]<a[i]的
for (int j = 1; j <= n; j++)
{
if (a[i] == b[j])
f[j] = max(f[j], maxv + 1); //更新答案
if (b[j] < a[i])
maxv = max(maxv, f[j]);//更新maxv
}
}
int res = 0;
for (int i = 1; i <= n; i++)
res = max(res, f[i]);
cout<<res<<endl;
}