📅  最后修改于: 2023-12-03 15:40:28.275000             🧑  作者: Mango
给定一个长度为 $n$ 的排列 $p$ 和一个长度相等的排列 $q$,现在你可以对排列 $p$ 进行任意次如下操作:
选择排列 $p$ 中的一个位置 $i$,然后交换 $p_i$ 和 $p_{i+1}$ 的位置。
你希望将排列 $p$ 变成排列 $q$,现在请你计算出最少需要进行多少次操作可以完成这个目标。
第一行一个整数 $n$,表示排列的长度。
第二行 $n$ 个空格隔开的整数 $p_i$,表示排列 $p$。
第三行 $n$ 个空格隔开的整数 $q_i$,表示排列 $q$。
输出一个整数,表示最少操作次数。
$1 \leq n \leq 1000$,$p_i$ 和 $q_i$ 均为 $1$ 到 $n$ 之间的整数。
5
1 2 3 5 4
5 2 3 4 1
2
本道题我们采用贪心和双指针算法来解决。我们先找出两个排列中相同数字的位置,假如相同数字在位置 $a,b$ 和 $c,d$ 分别出现,我们可以通过 $2$ 次操作,使得 $a,b$ 前的数字和 $c,d$ 前的数字分别相同,之后问题就被分解成了规模更小的子问题,由于一些限制,我们又发现了新的贪心策略,使得 $O(n^2)$ 的时间复杂度可以优化至 $O(n \log n)$ 的时间复杂度,详情请看下面的代码实现。此外要注意,题目数据范围比较小,不能直接使用归并排序,否则会超时,应该手写归并排序来避免 TLE。
n = int(input())
p = list(map(int, input().split()))
q = list(map(int, input().split()))
locx = [0] * (n + 1)
locy = [0] * (n + 1)
for i in range(n):
locx[p[i]] = i
locy[q[i]] = i
cnt = 0
for i in range(1, n + 1):
while locx[i] != locy[i]:
j = locy[i]
k = p[j - 1]
locx[k], locx[k + 1], locx[i] = locx[k + 1], locx[k], j - 1
p[j - 1], p[j] = p[j], p[j - 1]
cnt += 1
print(cnt)
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=1e5+10, M=1e6+10;
const int INF=0x3f3f3f3f;
int n, a[N], b[N], pa[N], pb[N], res;
void merge_sort(int l, int r)
{
if(l>=r) return ;
int mid=l+r>>1;
merge_sort(l, mid), merge_sort(mid+1, r);
int i=l, j=mid+1, idx=l;
while(i<=mid && j<=r)
if(a[i]<=a[j]) pb[idx]=pa[i], b[idx++]=a[i++];
else pb[idx]=pa[j], b[idx++]=a[j++];
while(i<=mid) pb[idx]=pa[i], b[idx++]=a[i++];
while(j<=r) pb[idx]=pa[j], b[idx++]=a[j++];
for(int i=l; i<=r; i++) a[i]=b[i], pa[i]=pb[i];
}
int main(void)
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
pa[i]=i;
}
res=0;
merge_sort(1, n);
for(int i=1; i<=n; i++)
if(pa[i]!=i)
{
res++;
swap(pa[i], pa[a[i]]);
//printf("%d %d\n", i, a[i]);
swap(a[i], a[pa[i]]);
}
printf("%d\n", res);
return 0;
}