描述
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。不过,只要告诉奶牛它的长度就可以了。数列A和B的长度均不超过3000。
输入格式
第一行N,表示A,B的长度。
第二行,串A。
第三行,串B。
输出格式
输出长度。
样例输入
4
2 2 1 3
2 1 2 3
样例输出
2
数据范围与约定
1<=N<=3000,A,B中的数字不超过2^31-1
我们用
F
[
i
,
j
]
F[i,j]
F[i,j] 表示
A
1
,
⋯
 
,
A
i
A_1,\cdots ,A_i
A1,⋯,Ai 与
B
1
,
⋯
 
,
B
j
B_1,\cdots,B_j
B1,⋯,Bj 可以构成的以
B
j
B_j
Bj 为结尾的LCIS的长度:
F
[
i
,
j
]
=
{
F
[
i
−
1
,
j
]
,
A
i
≠
B
j
max
0
≤
k
<
j
,
B
k
<
B
j
{
F
[
i
−
1
,
k
]
}
+
1
=
max
0
≤
k
<
j
,
B
k
<
A
i
{
F
[
i
−
1
,
k
]
}
+
1
,
A
i
=
B
j
F[i,j]=\begin{cases}F[i-1,j],\quad A_i\neq B_j\\\max\limits_{0\leq k<j,B_k<B_j}\{F[i-1,k]\}+1=\max\limits_{0\leq k<j,B_k<A_i}\{F[i-1,k]\}+1,\quad A_i=B_j\end{cases}
F[i,j]=⎩⎨⎧F[i−1,j],Ai̸=Bj0≤k<j,Bk<Bjmax{F[i−1,k]}+1=0≤k<j,Bk<Aimax{F[i−1,k]}+1,Ai=Bj
在转移过程中,我们把满足
0
≤
k
<
j
,
B
k
<
A
i
0\le k<j,B_k<A_i
0≤k<j,Bk<Ai 的
k
k
k 构成的集合称为
F
[
i
,
j
]
F[i,j]
F[i,j] 进行状态转移时的决策集合,记为
S
(
i
,
j
)
S(i,j)
S(i,j) 。注意到,在第二层循环
j
j
j 从
1
1
1 增加到
n
n
n 时,
B
k
<
A
i
B_k<A_i
Bk<Ai是固定的。因此当变量
j
j
j 增加
1
1
1 时,
k
k
k 的取值范围从
0
≤
k
<
j
0\le k<j
0≤k<j 变为
0
≤
k
<
j
+
1
0\le k<j+1
0≤k<j+1,即整数
j
j
j 可能会进入新的决策集合。也就是说,我们只需要
O
(
1
)
O(1)
O(1) 地检查条件
B
j
<
A
i
B_j<A_i
Bj<Ai 是否满足,已经在决策集合中的数则一定不会被去除:
S
(
i
,
j
+
1
)
=
{
S
(
i
,
j
)
B
j
≥
A
i
S
(
i
,
j
)
∪
{
j
}
B
j
<
A
i
S(i,j+1)=\begin{cases}S(i,j)\quad B_j\geq A_i\\S(i,j)\cup \{j\}\quad B_j<A_i\end{cases}
S(i,j+1)={S(i,j)Bj≥AiS(i,j)∪{j}Bj<Ai
最终目标是
max
1
≤
j
≤
m
F
[
n
,
j
]
\max\limits_{1\leq j\leq m}F[n,j]
1≤j≤mmaxF[n,j] 。
摘自李煜东《算法竞赛进阶指南》。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e3+10;
int f[N][N],a[N],b[N],n,ans;
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int j=1;j<=n;j++)scanf("%d",&b[j]);
for(int i=1;i<=n;i++)
{
int val=0;//决策集合S(i,j)中f[i-1][k]的最大值
if(b[0]<a[i])val=f[i-1][0];//j=1时,0可以作为k的取值
for(int j=1;j<=n;j++)
{
if(a[i]==b[j])f[i][j]=val+1;
else f[i][j]=f[i-1][j];
if(b[j]<a[i])val=max(val,f[i-1][j]);
//j即将增大为j+1,检查j能否进入新的决策集合
}
}
for(int i=1;i<=n;i++)ans=max(ans,f[n][i]);
printf("%d\n",ans);
return 0;
}
总结
最长公共上升子序列,很经典。