##J - Yet Another Sorting Problem
题目:
给出一个长度为N+M的序列p,它是(1,2…,N+M)的一个置换。
p的第i项是pi。你可以做以下任何次数的操作。
在1到N(包括)之间选择一个整数n,在1到M(包括)之间选择一个整数m。然后,交换 pn和pN+m。
找出将p按升序排序所需的最少操作数。我们可以证明,有可能对 p 在这个问题的约束条件下,可以按升序排序。
限制条件
输入的所有数值都是整数。
1 ≤ N , M ≤ 10 5 1 ≤ p i ≤ N + M p 是一个 ( 1 , 2 … , N + M ) .
输入
输入是由标准输入提供的,格式如下。
N M
p 1 ⋯ p N + M
输出
打印排序所需的最小操作数
p升序排序所需的最小操作数。
样本1
输入
2 3
1 4 2 5 3
输出
3
样本2
输入
5 7
9 7 12 6 1 11 2 10 3 8 4 5
样本输出2
10
题目思路:
最原始情况下认为n+m个数需要交换n+m次,通过每一位上的数字我们可以把数组分成几个环,通过加上或减去相应操作得出题目所求的最小操作数
其中几种特殊情况分开讨论
- pi恰好在i位置上时不需要交换
- 环中既包含左边数组的数也包含右边数组的数:
交换到环中最后两个数时,对调正好回到自己的位置上,这种情况的环需要的交换次数为环内元素总数-1 - 环中只包含一边数组的元素:
需要借助另一边数组的元素再进行归位的调换,因此需要多一步,这种情况交换次数为环内个数+1
注:
- 代码中的环开始都默认为第二种,因此在最后同时减掉了3中的情况(即本应+1的情况也-1),所以在最后加上的情况3的个数时要乘2
- 在最后判断情况3的环时取最大值,因为在既有左边单独成环又有右边单独成环的情况下,通过一次借助就可以让两边都进行交换
#include <iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#include<map>
#include<queue>
#define int long long
const int N=1e6+10,INF=0x3f3f3f3f,mod=998244353;
using namespace std;
int a[N];
int n,m;
int cnt,x1,x2;
bool st[N];//标记当前位置的数是否判断过
signed main()
{
// ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1; i<=n+m; i++)
{
cin>>a[i];
if(a[i]==i)
{
cnt++;//情况1中已经归位的数不需要再交换
st[i]=1;
}
}
int j,cnt1=0,cnt2=0;
for(int i=1; i<=n+m; i++)
{
if(st[i]) continue;
for(j=i; !st[j]; j=a[j])//找环
{
if(j<=n) cnt1++;//当前数字为左边数组的
else cnt2++;//当前数字为右边数组的
st[j]=1;
}
cnt++;//分成了几个环,每个环可以匹配次数减一
if(!cnt2)//在同一数组里的,需要借助已经换完的数交换
{
x1++;
}
else if(!cnt1)
{
x2++;
}
cnt1=0,cnt2=0;
}
printf("%d",n+m-cnt+2*max(x1,x2));
return 0;
}
by 辰
感谢浏览~