题目
解题思路
闫氏DP分析法:
状态表示 f[i][j]—集合:
考虑 a 中前 i 个数字,b 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案
状态表示 f[i][j]—属性:
该方案的子序列长度最大 max
状态转移 :
1.a[i]!=b[j]
从考虑 a数组 中前 i-1 个数字, b数组 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案转移过来:
f i , j = m a x ( f i , j f i − 1 , j ) f_{i,j}=max(f_{i,j}\,f_{i−1,j}) fi,j=max(fi,jfi−1,j)
2.a[i]==b[j]
从考虑 a数组 中前 i 个数字, b数组 中前 k 个数字 ,且当前以 b[k] 结尾的子序列的方案转移过来:
f
i
,
j
=
m
a
x
(
f
i
,
j
,
f
i
−
1
,
k
+
1
)
k
∈
[
0
,
j
−
1
]
,
b
j
>
b
k
fi,j=max(f_{i,j},f_{i−1,k}+1) \ k∈[0,j−1],b_j>b_k
fi,j=max(fi,j,fi−1,k+1) k∈[0,j−1],bj>bk
初始状态:f[0][0]
目标状态:f[n][i]
如果按照上面思路实现需要三重循环
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= n; ++ j)
{
f[i][j] = f[i - 1][j];
if (a[i] == b[j])
{
for (int k = 0; k < j; ++ k)
{
if (b[j] > b[k])
{
f[i][j] = max(f[i][j], f[i - 1][k] + 1);
}
}
}
}
}
然后我们发现对于第二种状态转移是找到 0 ~ j -1之间,值比b[j]小的最大值,而我们发现每次出现一个新的j时,都从头开算是重复运算(因为本层内i还没有变化),不划算。那怎么才能优化呢?那就是在本层循环中用一个变量记录比a[i]小的b[k]最大值,这样就不用每次现算了,直接拿来就用了
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3010;
int f[N][N], a[N], b[N];
int n;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) scanf("%d", &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]);
printf("%d\n", res);
return 0;
}
如有错误,请指正。