LCIS:把结尾值写进状态
LCIS 同时有两个要求:它既是公共子序列,又严格递增。如果我们直接套用 LCS 的定义,会发现只记录两个前缀的答案不足以转移,因为新元素时还必须知道当前子序列的结尾值。
所以借用 LIS 的定义结合一下:f[i][j] 表示只使用 A1…Ai,并且以 Bj 结尾的 LCIS 长度。于是我们把“结尾位置”写进状态后,递增限制就能通过比较 Bj 处理。
接着考虑转移从 i−1 推到 i 时:
- 若 Ai=Bj,不能用 Ai 与 Bj 配对,继承
f[i-1][j];
- 若 Ai=Bj,则在所有满足 Bk<Ai 的旧状态后接上当前值:
f[i][j]=1+k<j, Bk<Aimaxf[i−1][k]
但是如果直接枚举 k 会得到 O(n3),我们发现对于 k 这一层循环来说,i 其实是一个常数,所以我们直接直接找到满足 Bj<Ai 的最大的 f[i-1][j],因为 j 在逐渐扩展,所以可以直接省掉第三层循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int a[maxn], b[maxn]; int f[maxn][maxn];
void solve( ) { int n = read( ); for( int i = 1; i <= n; ++ i ) a[i] = read( ); for( int i = 1; i <= n; ++ i ) b[i] = read( );
for( int i = 1; i <= n; ++ i ) { int mx = 0; for( int j = 1; j <= n; ++ j ) { if( a[i] != b[j] ) f[i][j] = f[i-1][j]; else f[i][j] = mx + 1; if( b[j] < a[i] ) mx = max( mx, f[i-1][j] ); } } int ans = 0; for( int j = 1; j <= n; ++ j ) ans = max( ans, f[n][j] ); cout << ans << '\n'; }
|
- 时间复杂度:O(n2);
- 当前实现空间复杂度:O(n2);
记录
这题保留为 C 类记录。它把 LIS 中的“结尾值”与 LCS 中的“双序列匹配”结合起来;优化的关键不是换数据结构,而是在固定 Ai 后用一次顺序扫描维护合法前驱的最优值。