luoguP1439,本身不算难,记一下防止自己下次看到不会做 qwq
# 题意
给出 1 到 n 的两组全排列,求它们的最长公共子序列。
# 最长上升子序列 LIS
朴素做法:f [i] 到第 i 个元素的 LIS 长度,n 方做即可。
二分做法:f [i] 表示长度为 i 的上升子序列,每次对于一个新的元素,二分查出比这个值大的第一个值,去更新 f 数组。
rep (i, 1, n) | |
{ | |
int l = 0, r = mx, mid; | |
// 可以扩展最大长度 | |
if (a[i] > f[mx]) | |
f[++mx] = a[i]; | |
else | |
{ | |
while (l < r) | |
{ | |
mid = (l + r) >> 1; | |
if (f[mid] > a[i]) | |
r = mid; | |
else | |
l = mid + 1; | |
} | |
// 更新该长度对应的最小值 | |
f[l] = min(f[l], a[i]); | |
} | |
} |
# 最长公共子序列 LCS
a,b 两数组对应的元素相同,位置不同。
直接找 b 数组每个元素对应在 a 数组中的位置,求这个位置的 LIS 即可。
#include <cstdio> | |
#include <iostream> | |
#define rep(i,s,t) for(int i=s;i<=t;i++) | |
using namespace std; | |
const int MAXN = 1e5 + 233; | |
int n, a[MAXN], b[MAXN], pos[MAXN], f[MAXN]; | |
int main() | |
{ | |
// freopen("in.txt", "r", stdin); | |
scanf("%d", &n); | |
rep (i, 1, n) | |
{ | |
scanf("%d", &a[i]); | |
pos[a[i]] = i; //pos 记录每个数字在 a 中的位置 | |
f[i] = 0x3f3f3f3f; | |
} | |
rep (i, 1, n) | |
scanf("%d", &b[i]); | |
int mx = 0; | |
rep (i, 1, n) | |
{ | |
int l = 0, r = mx, mid; | |
if (pos[b[i]] > f[mx]) | |
f[++mx] = pos[b[i]]; | |
else | |
{ | |
while (l < r) | |
{ | |
mid = (l + r) >> 1; | |
if (f[mid] > pos[b[i]]) | |
r = mid; | |
else | |
l = mid + 1; | |
} | |
f[l] = min(f[l], pos[b[i]]); | |
} | |
} | |
printf("%d\n", mx); | |
return 0; | |
} |