SDUT 2021 Summer Individual Contest - 6补题

##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次,通过每一位上的数字我们可以把数组分成几个环,通过加上或减去相应操作得出题目所求的最小操作数

其中几种特殊情况分开讨论

  1. pi恰好在i位置上时不需要交换
  2. 环中既包含左边数组的数也包含右边数组的数:
    交换到环中最后两个数时,对调正好回到自己的位置上,这种情况的环需要的交换次数为环内元素总数-1
  3. 环中只包含一边数组的元素:
    需要借助另一边数组的元素再进行归位的调换,因此需要多一步,这种情况交换次数为环内个数+1

注:

  1. 代码中的环开始都默认为第二种,因此在最后同时减掉了3中的情况(即本应+1的情况也-1),所以在最后加上的情况3的个数时要乘2
  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 辰

                    

感谢浏览~

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值