题目描述
给定两个长度分别为n和m的序列A和B,求两个序列的最长公共上升子序列长度。
例如:
1 5 3 6 3 2 7 3 6 2 和 9 6 2 3 1 5 3 3 6 1 的最长上升子序列的长度为3。
题目分析
这是一道LIS和LCS的综合题目。
【状态定义】:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示序列
A
[
1
:
i
]
A[1:i]
A[1:i]和序列
B
[
1
:
j
]
B[1:j]
B[1:j]构成的以
B
[
j
]
[
B[j][
B[j][为结尾的LCIS长度。
【状态转移】 为:
- A [ i ] ≠ B [ j ] A[i] ≠ B[j] A[i]=B[j] : d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i−1][j] (可以理解为将 A [ i ] A[i] A[i]扔掉)
-
A
[
i
]
=
B
[
j
]
A[i] = B[j]
A[i]=B[j] :
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
−
1
]
[
k
]
}
dp[i][j] = max\{ dp[i-1][k]\}
dp[i][j]=max{dp[i−1][k]} ,
k
k
k 满足:
k
∈
[
0
,
j
)
,
B
[
k
]
<
B
[
j
]
k∈[0,j) ,B[k]<B[j]
k∈[0,j),B[k]<B[j]
d p [ ] [ j ] dp[][j] dp[][j]的子问题不是 d p [ ] [ j − 1 ] dp[][j-1] dp[][j−1],而是满足单调性的 d p [ ] [ k ] dp[][k] dp[][k]
【边界条件】:
d
p
[
0
]
[
j
]
=
d
p
[
i
]
[
0
]
=
0
dp[0][j] = dp[i][0] = 0
dp[0][j]=dp[i][0]=0
【目标状态】:
m
a
x
{
d
p
[
n
]
[
j
]
}
max\{dp[n][j]\}
max{dp[n][j]},
j
∈
[
1
,
m
]
j ∈[1,m]
j∈[1,m]
代码
int cal(){
a[0] = b[0] = -inf;
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(int k = 0;k<j;k++){ //注意k=0开始
if(b[k]<b[j])
dp[i][j] = max(dp[i][j],dp[i-1][k]+1);
}
}
}
}
int ans=-inf;
for(int j=1;j<=m;j++) ans = max(ans,dp[n][j]);
return ans;
}
【细节注意】
当
B
B
B串当中,找不到
B
[
k
]
B[k]
B[k]满足
B
[
k
]
<
B
[
j
]
B[k]<B[j]
B[k]<B[j]时,
B
[
0
]
B[0]
B[0]充当满足条件的
B
[
k
]
B[k]
B[k],那么对于
B
B
B串来说,以
B
[
j
]
B[j]
B[j]为结尾的LIS长度为1,即
B
[
j
]
B[j]
B[j]本身。因此:
- A [ 0 ] A[0] A[0]和 B [ 0 ] B[0] B[0] 定义为-∞。
- k k k从 0 0 0开始枚举
优化
注意观察状态转移公式,当
A
[
i
]
=
B
[
j
]
A[i] = B[j]
A[i]=B[j]时,决策集合为$ dp[i-1][k]
,
,
,k
满
足
满足
满足k∈[0,j) ,B[k]<B[j]$ ,由于
A
[
i
]
=
B
[
j
]
A[i] = B[j]
A[i]=B[j],即为满足
k
∈
[
0
,
j
)
,
B
[
k
]
<
A
[
i
]
k∈[0,j) ,B[k]<A[i]
k∈[0,j),B[k]<A[i]。 在内层循环中,
i
i
i 的值不变,
j
j
j的值递增。即决策集合的改变只受
B
[
j
]
B[j]
B[j]的影响,如果
B
[
j
]
<
A
[
i
]
B[j]<A[i]
B[j]<A[i],则
d
p
[
i
−
1
]
[
j
]
dp[i-1][j]
dp[i−1][j]进入决策集合,否则决策集合不变。
定义变量
v
v
v为每轮最初的决策集合结果值,
j
j
j递增前,判断:
- B [ j ] < A [ i ] B[j]<A[i] B[j]<A[i], v = m a x ( v , d p [ i − 1 ] [ j ] ) v = max(v, dp[i-1][j]) v=max(v,dp[i−1][j])
- B [ j ] ≥ A [ i ] B[j] ≥ A[i] B[j]≥A[i], v v v不变
代码
int cal(){
a[0] = b[0] = -inf;
for(int i=1;i<=n;i++){
int v=0;
for(int j=1;j<=m;j++){
if(a[i] != b[j]) dp[i][j] = dp[i-1][j];
else dp[i][j] = v+1;
if(b[j] < a[i]) v = max(v,dp[i-1][j]);
}
}
int ans=-inf;
for(int j=1;j<=m;j++) ans = max(ans,dp[n][j]);
return ans;
}