题目描述
给出 1,2,\ldots,n1,2,…,n 的两个排列 P_1P1 和 P_2P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 nn。
接下来两行,每行为 nn 个数,为自然数 1,2,\ldots,n1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例
输入 #1复制
5
3 2 1 4 5
1 2 3 4 5
输出 #1复制
3
说明/提示
n<100000;
//递归,存在大量重复子问题计算,需要动态规划
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005], b[100005], n;
//int dp[1];
int solve(int i, int j)
{
//返回1-i,1-j的最长公共子序列
int l=0, r=0;
if (i == 0 || j == 0)return 0;
l = solve(i - 1, j);
r = solve(i, j - 1);
if (a[i] == b[j]) {
return solve(i-1,j-1) + 1;
}
return max(l, r);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) {
//cin >> a[i];
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
//cin >> b[i];
scanf("%d", &b[i]);
}
cout<<solve(n,n);
return 0;
}
根据递归代码写动态规划
//数据太大需要状态压缩,依然无法解决
#include<iostream>
#include<algorithm>
using namespace std;
int a[100005], b[100005], n;
int dp[100005];
int main()
{
int temp=0,cur;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
//scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
cin >> b[i];
//scanf("%d", &b[i]);
}
for (int i = 1; i <= n; ++i) {
temp = 0;
for (int j = 1; j <= n; ++j) {
cur = dp[j];
if (a[i] == b[j])dp[j] = temp + 1;
else {
dp[j] = max(dp[j], dp[j-1]);
}
temp = cur;
}
}
cout << dp[n];
return 0;
}
以上可求正常最长公共序列,本题由于太长只能得一半分,
由于这题是1到n的全排列,两个串的元素完全相同且不重复,所以可以将两个串离散化,例如379(一串)变为123将793(二串)就变为231;所以一串离散为一个单调增的数列,如果二串相应数字和一串顺序相同那就也是相应增的,所以题目就变成了,求二串最长单调增序列o(nlogn)。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int a[100005], b[100005];
int dp[10000], n, m;
int main()
{
cin >> n;
vector<int>ans(1, 999999999);
for (int i = 0; i < n; ++i) {
cin >> m;
a[m] = i;
}
for (int i = 0; i < n; ++i) {
cin >> m;
b[i] = a[m];
}
for (int i = 0; i < n; ++i) {
if (b[i] > ans.back()) {
ans.push_back(b[i]);
}
else {
*lower_bound(ans.begin(),ans.end(),b[i]) = b[i];
}
}
cout << ans.size();
return 0;
}