poj 3270 Cow Sorting (置换入门)

题意:给你一个无序数列,让你两两交换将其排成一个非递减的序列,每次交换的花费交换的两个数之和,问你最小的花费

思路:首先了解一下什么是置换,置换即定义S = {1,...,n}到其自身的一个双射函数f。那么我们将其写为若干个不相交的循环的乘积形式(A1, A2, ... Ap1)(B1, B2, ... Bp2)... ...例如

             数组   9 4 5 7 3 1  

             下标   1 2 3 4 5 6

    排序后下标   6 3 4 5 2 1

让我们看下标 1->6->1 一个循环 (9 1)

2->3->4->5->2       一个循环(4 5 7 3)、

循环节内的每一个元素都在不合适的位置上,因此长度为l循环节内部至少需要进行(l - 1)次互换可使其有序。 

我们可以用一个贪心的思想,每次用循环最小的那个数进行两两交换,定义循环的所有数总和为sum,循环的最小值为k,循环长度为len,那么一个循环的花费为 sum-k+(len-1)*k => sum+(len-2)*k

但是其实还有另一种可能,用整个置换最小的那个和这个循环最小的进行换位,定义循环的所有数总和为sum,循环的最小值为k,循环长度为len,整个置换的最小值为kmin ,,那么一个循环的花费为 sum+(len+1)*kmin+k

每一次循环的最小值为 ans=ans+min(sum+(len-2)*k,sum+(len+1)*kmin+k)

组合数学置换资料https://wenku.baidu.com/view/9b8d9d32e87101f69e3195f8.html

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define maxn 100005
using namespace std;

struct  node
{
    int num;
    int id;
};

bool cmp(node a,node b)
{
    if(a.num<b.num) return true;
    else return false;
}

int main()
{
    node data[maxn];
    int visit[maxn];
    int n;
    int minn=1<<25;
    int ans=0;
    while(scanf("%d",&n)!=-1)
    {
      for(int i=0;i<n;i++)
      {
        scanf("%d",&data[i].num);
        data[i].id=i;
        minn=min(minn,data[i].num);
      }
      memset(visit,0,sizeof(visit));
      sort(data,data+n,cmp);
      for(int i=0;i<n;i++)
      {
        if(!visit[i])
        {
            visit[i]=1;
            int tmp=data[i].id;
            int kmin=data[i].num;
            int sum=data[i].num;
            int len=1;
            while(tmp!=i)
            {
               visit[tmp]=1;
               len++;
               sum+=data[tmp].num;
               kmin=min(kmin,data[tmp].num);
               tmp=data[tmp].id;
            }
            ans+=min(sum+kmin*(len-2),sum+(len+1)*minn+kmin);
        }
      }
      printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2017-03-28 18:29  simpleknight  阅读(199)  评论(0编辑  收藏  举报