题目描述
给出 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n 的两个排列 P 1 P_1 P1 和 P 2 P_2 P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 n n n。
接下来两行,每行为 n n n 个数,为自然数 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例 #1
输入 #1
5
3 2 1 4 5
1 2 3 4 5
输出 #1
3
说明/提示
- 对于 50 % 50\% 50% 的数据, n ≤ 1 0 3 n \le 10^3 n≤103;
- 对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \le 10^5 n≤105。
题目分析
类似题目:友好城市。
把题目转化一下:
每一个数只会在两个串之间出现一次,我们要将这两个数字连起来,这样就会产生一条线段,每两条线段之间不能有交叉,求解最多可添加的线段条数
如何添加更多线段?
假设现在有两个线段,端点分别为 x 1 , y 1 x_1,y_1 x1,y1 和 x 2 , y 2 x_2,y_2 x2,y2 能添加的条件就是 $x_1 < x_2 $ 且 y 1 < y 2 y_1 < y_2 y1<y2
这样只需要让两个端点的值尽量小并且都保持从小到大的顺序
这不就是最长上升子序列吗!
可以以任意一个位置为关键字由小到大排序,再求另一个关键字所组成的最长不下降子序列长度,输出即可
定义: d p [ i ] dp[i] dp[i] 长度为 i i i 的最长上升子序列的最小结尾
算法: 二分减少时间复杂度
for(int i = 1 ; i <= n ; i++){if(cty[i].second > dp[cnt])dp[++cnt] = cty[i].second;//比最大的还大,直接加一个新的else *lower_bound(dp+1,dp+cnt+1,cty[i].second) = cty[i].second;//找到第一个比他大的替换
}
AC代码
#include<bits/stdc++.h>using namespace std;const int SIZN = 1e5 + 10;int n;
pair<int,int> cty[SIZN];
int dp[SIZN],cnt = 0;
bool cmp(pair<int,int> a,pair<int,int> b){return a.first < b.first;
}
signed main(){cin >> n;for(int i = 1 ; i <= n ; i++){int k;cin >> k;cty[k].first = i;}for(int i = 1 ; i <= n ; i++){int k;cin >> k;cty[k].second = i;}sort(cty+1,cty+n+1,cmp);for(int i = 1 ; i <= n ; i++){if(cty[i].second > dp[cnt])dp[++cnt] = cty[i].second;else *lower_bound(dp+1,dp+cnt+1,cty[i].second) = cty[i].second;}cout << cnt;return 0;
}