题意
- 给你两个排列 A , B A, B A,B,每次可以从一个排列中拿出其中的第一个数,当两个排列的第一个数不相同时可以同时拿出两个排列的第一个数,求最少多少次能把所有数拿完, n < = 1 e 6 n<=1e6 n<=1e6
不难想到设 d p [ i ] [ j ] dp[i][j] dp[i][j]为 A A A序列取到了 i i i, B B B序列取到了 j j j的最小步数
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + 1 dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1 dp[i][j]=min(dp[i−1][j],dp[i][j−1])+1
如果 A [ i ] ! = B [ j ] , d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − 1 ] + 1 ) A[i] != B[j], dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1) A[i]!=B[j],dp[i][j]=min(dp[i][j],dp[i−1][j−1]+1)
这样子是 n 2 n^2 n2的,而且状态数就有 O ( n 2 ) O(n^2) O(n2)个,考虑优化状态数
发现 A [ i ] ! = A [ j ] A[i] != A[j] A[i]!=A[j]时 状态是可以合并的
那么我们来考虑一下压缩状态
设 f i f_i fi为 A A A取到 A i A_i Ai, B B B取到与 A i A_i Ai相同的数(当前取了 A i A_i Ai或 B j B_j Bj中的一个)
这个时候取完所有数的最优答案
设 B B B取到了 B j B_j Bj, g [ d ] g[d] g[d]表示与当前这位最接近的出现了距离差为 d d d的 i i i的位置
那么可以得到以下状态转移方程
f
[
i
]
=
f
[
g
[
i
−
j
−
1
]
]
+
g
[
i
−
j
−
1
]
−
i
+
1
(
g
[
i
−
j
−
1
]
!
=
0
)
f[i] = f[g[i - j - 1]] + g[i - j - 1] - i + 1\ (g[i - j - 1] != 0)
f[i]=f[g[i−j−1]]+g[i−j−1]−i+1 (g[i−j−1]!=0)
f
[
i
]
=
m
a
x
(
i
+
1
,
j
)
(
g
[
i
−
j
−
1
]
=
0
)
f[i] = max(i + 1, j) \ (g[i - j - 1] = 0)
f[i]=max(i+1,j) (g[i−j−1]=0)
f
[
i
]
=
f
[
g
[
i
−
j
+
1
]
]
+
g
[
i
−
j
+
1
]
−
i
(
g
[
i
−
j
+
1
]
!
=
0
)
f[i] = f[g[i - j + 1]] + g[i - j + 1] - i\ (g[i - j + 1] != 0)
f[i]=f[g[i−j+1]]+g[i−j+1]−i (g[i−j+1]!=0)
f
[
i
]
=
m
a
x
(
i
,
j
+
1
)
(
g
[
i
−
j
+
1
]
=
0
)
f[i] = max(i, j + 1) (g[i - j + 1] = 0)
f[i]=max(i,j+1)(g[i−j+1]=0)
因为 A [ i ] ! = B [ j ] A[i] != B[j] A[i]!=B[j]的状态已经被合并了
那么剩下的状态就只剩下 A [ i ] = B [ j ] A[i] = B[j] A[i]=B[j]的了
所以剩下的转移就只有从 ( i − 1 , j ) (i - 1, j) (i−1,j)和 ( i , j − 1 ) (i, j - 1) (i,j−1)两种了
这样子直接做就差不多可以了
复杂度 Θ ( n ) \Theta(n) Θ(n)
Codes
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
const int N = 1e6 + 10;
__gnu_pbds::cc_hash_table<int, int> g;
int f[N], a[N], b[N], pos1[N], pos2[N], n;
int main() {
#ifdef ylsakioi
freopen("eternal.in", "r", stdin);
freopen("eternal.out", "w", stdout);
#endif
scanf("%d", &n);
for(int i = 1; i <= n; ++ i)
scanf("%d", &a[i]), pos1[a[i]] = i;
for(int i = 1; i <= n; ++ i)
scanf("%d", &b[i]), pos2[pos1[b[i]]] = i;
for(int i = n; i >= 1; -- i) {
int j = pos2[i]; f[i] = INT_MAX;
f[i] = min(f[i], g[i - j - 1] ? f[g[i - j - 1]] + (g[i - j - 1] - i + 1) : max(n - i + 2, n - j + 1));
f[i] = min(f[i], g[i - j + 1] ? f[g[i - j + 1]] + (g[i - j + 1] - i) : max(n - i + 1, n - j + 2));
g[i - j] = i;
}
//cout << g[0] << ' ' << f[g[0]] << endl;
printf("%d\n", g[0] ? f[g[0]] + g[0] - 1: n);
return 0;
}