P1439 两个排列的最长公共子序列-普及+/提高
P1439 两个排列的最长公共子序列
题目描述
给出 1,2,…,n1,2,\ldots,n1,2,…,n 的两个排列 P1P_1P1 和 P2P_2P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 nnn。
接下来两行,每行为 nnn 个数,为自然数 1,2,…,n1,2,\ldots,n1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例 #1
输入 #1
5
3 2 1 4 5
1 2 3 4 5
输出 #1
3
说明/提示
- 对于 50%50\%50% 的数据, n≤103n \le 10^3n≤103;
- 对于 100%100\%100% 的数据, n≤105n \le 10^5n≤105。
solution
动态规划
-
设两个序列为 a,b 如果定义 f[i] 为 a, b 中 b[i]为结尾的公共子序列的最大长度,则 f[i]为 a 中在b[i]之前那些数j的最大的f[j] + 1
-
1 定义公式
-
f[i] 以 b[i] 为结尾的最长公共子序列长度
-
-
2 递推关系
-
f[i] = f[j] + 1
-
f[j] 为 a 中,b[i] 左侧的最大的f[k] k = 1...i-1
-
-
3 结果
-
ans = max(f[i])
-
-
4 可以借助树状数组在 log n 时间内求出 max(f[k])
其实如果以 b[i] 在, a中的位置作为x[i]的话,最长公共子序列其实x[i]就是最大递增子序列,同样可以用dp求出
代码
#include "cstdio"
#include "iostream"
#include "vector"
#include "cstring"
#include "queue"
#include "algorithm"
#include "unordered_map"
#include "cmath"#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-folding-constant"
using namespace std;/** 题目大意:1-n 的两个排列,求最长公共子序列* 数据范围: n <= 10^5** 思路:动态规划* 设两个序列为 a,b 如果定义 f[i] 为 a, b 中 b[i]为结尾的公共子序列的最大长度,则 f[i]为 a 中在b[i]之前那些数j的最大的f[j] + 1* * 1 定义公式* f[i] 以 b[i] 为结尾的最长公共子序列长度* 2 递推关系* f[i] = f[j] + 1* f[j] 为 a 中,b[i] 左侧的最大的f[k] k = 1...i-1* 3 结果* ans = max(f[i])** 4 可以借助树状数组在 log n 时间内求出 max(f[k])* * 其实如果以 b[i] 在, a中的位置作为x[i]的话,最长公共子序列其实x[i]就是最大递增子序列,同样可以用dp求出* */const int N = 1e5 + 5;
int n, a[N], b[N], c[N], pos[N];int low_bit(int x) {return x & -x;
}void add(int x, int v) {while (x <= n) {c[x] = max(c[x], v);x += low_bit(x);}
}int sum(int x) {int ans = 0;while (x > 0) {ans = max(ans, c[x]);x -= low_bit(x);}return ans;
}int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%d", a + i), pos[a[i]] = i;for (int i = 1; i <= n; i++) scanf("%d", b + i);int ans = 0;for (int i = 1; i <= n; i++) {int x = sum(pos[b[i]] - 1);add(pos[b[i]], x + 1);ans = max(ans, x + 1);}printf("%d\n", ans);return 0;
}#pragma clang diagnostic pop