neu 1493 Caoshen like math(最小交换次数)

http://acm.neu.edu.cn/hustoj/problem.php?cid=1047&pid=4

题意:数字1到n 任意排列

        求排列成有序序列最少交换次数

思路:求最小交换次数有两种

        1 交换的两数必须相邻  (poj 2299)

           通过归并排序求出其逆序数即为所求值 

           证明:可以先将最大数交换到最后,由于是相邻两个数交换,需要交换的次数为最大数后面的数的个数(可以看做是最大数的逆序数),然后,交换过后,去除最大数,再考虑当前最大数也需要其逆序数次交换。则每个数都需要交换其逆序数次操作,则总最少交换次数为序列总体的逆序数。

  

       2 交换任意两数

          也就是此题

          这题有两种方法 

          1 第一种是每次将排完序后当前位置的值与现在当前位置的值交换 求出即为最小交换数

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int   a[1000000+10];
int vis[1000000+10];
int ans;
void swap(int &a,int &b)
{
    int temp=a;
    a=b;
    b=temp;
}
int main()
{
    int n;
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        ans=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            vis[a[i]]=i;
        }
        for(i=1;i<=n;i++)
        {
            int temp=a[i];
            if(a[i]!=i)
            {
                ans++;
                vis[temp]=vis[i];
                swap(a[i],a[vis[i]]);
                 
            }
        }
        printf("%d
",ans);
    }
    return 0;
}

          2 求出循环节个数 ans= n-循环节个数(根据之前的坐标和排序之后的坐标,构建成一个有向图)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int num[1000000+10][2];
int main()
{
    int n;
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        int to;
        int ans=0;//环的个数
        memset(num,0,sizeof(num));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&to);
            num[i][1]=1;
            num[to][0]=1;
            if(num[to][1]==1) ans++;
        }
        printf("%d
",n-ans);
    }
    return 0;
}

   

         

原文地址:https://www.cnblogs.com/sola1994/p/4265545.html