• 7-12 与零交换 (25 分)


    将 { 0, 1, 2, ..., N-1 } 的任意一个排列进行排序并不困难,这里加一点难度,要求你只能通过一系列的 Swap(0, *) —— 即将一个数字与 0 交换 —— 的操作,将初始序列增序排列。例如对于初始序列 { 4, 0, 2, 1, 3 },我们可以通过下列操作完成排序:

    • Swap(0, 1) ⟹ { 4, 1, 2, 0, 3 }
    • Swap(0, 3) ⟹ { 4, 1, 2, 3, 0 }
    • Swap(0, 4) ⟹ { 0, 1, 2, 3, 4 }

    本题要求你找出将前 N 个非负整数的给定排列进行增序排序所需要的最少的与 0 交换的次数。

    输入格式:

    输入在第一行给出正整数 N (≤);随后一行给出{ 0, 1, 2, ..., N-1 } 的一个排列。数字间以空格分隔。

    输出格式:

    在一行中输出将给定序列进行增序排序所需要的最少的与 0 交换的次数。

    输入样例:

    10
    3 5 7 2 6 4 9 0 8 1
    

    输出样例:

    9

    3 5 7 2 6 4 9 0 8 1
    0 1 2 3 4 5 6 7 8 9
    由于给出的是全排序,那么就可以看成把a[i]移到以a[i]下标的位置
    可以找循环节即有(3,0,7,2),(5,1,9,6,4),(8)三个
    第一个节(包含0)需要循环节长度-1次操作,即可把其它元素移到对应的位置
    第二个节(不包含0)需要循环节长度+1次操作,可以看成向循环节中加入0(操作次数加1),
    那么循环节长度加1(交换时次数要加1),总共比包含0的情况多操作两次
    第三个节是一个元素,那么不需要任何操作。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int vis[N],a[N],b[N];
    void dfs(int u,int f)
    {
        if(vis[u]) return;
        vis[u]=f;
        dfs(b[u],f);
    }
    int main()
    {
        int n,cnt=1;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]),b[a[i]]=i;
        for(int i=0;i<n;i++)
            if(!vis[i]) dfs(a[i],cnt),cnt++;
        map<int,int> ma;
        //for(int i=0;i<n;i++) printf("%d%c",vis[i],i==n-1?'
    ':' ');
        for(int i=0;i<n;i++) ma[vis[i]]++;
        int ans=0;
        for(int i=1;i<cnt;i++)
        {
            if(ma[i]==1) continue;//循环节里面只有一个元素
            if(i==vis[0]) ans+=ma[i]-1;//循环节里面有0元素
            else ans+=ma[i]+1;//循环节里面没有0元素
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    android 振动
    linux实用命令-查看文件夹的大小
    无显示屏的开发板抓屏
    传感器Sensor的使用-距离感应(听筒模式)
    4.4原生应用获取通话状态
    git服务器的使用
    (转)浅谈ANR及log分析ANR
    ubuntu下从软件中心安装软件时的软件缓存目录
    mysql————Innodb的可重复读的情况下如何避免幻读?
    MyISAM和Innodb的区别
  • 原文地址:https://www.cnblogs.com/zdragon1104/p/10501929.html
Copyright © 2020-2023  润新知