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?'\n':' ');
    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\n",ans);
    return 0;
}

 

posted @ 2019-03-09 16:57  zdragon  阅读(899)  评论(0编辑  收藏  举报