poj 3270 Cow Sorting 置换

  总共共10^5个数,取值在10^6之内

  离散化后与下标形成映射,转换成置换群

  然后就可以形成:

  有 n = 10^5 个位置, 每个位置有一头编号为 a[i]  (取值范围为[1,n] ) 的牛, 其愤怒值为 dep[ a[i] ]

  对于置换群求出其循环因子后. 因为置换的权值花费为 dep[i], dep[j]

  所以我们取最小的 dep, 例如循环因子 ( 1 , 4, 7, 8 ) 需要三次置换 , 我们可以取最小的 1, 其愤怒值为 dep[1] ( 我们按权值递增离散化过)

  所以三次置换分别为 ( 1, 4 ) ( 1, 7 ) ( 1, 8 )

  则三次花费为  ( dep[1] + dep[4] ) + ( dep[1] + dep[7] ) +( dep[1] + dep[8] ) 

  等价与  dep[1] * 3 + ( dep[7] + dep[8] + dep[4] )   

  我们在找循环因子时,就可以计算出 总和以及 当前循环因子的最小值.  通过 dep[ Min ] * ( num-1 ) +  ( sum(当前循环因子愤怒值总和) -  dep[Min] ) 

  其实这个思路是有问题的. 题目只要求最小花费,并未要求最小置换次数. 是否我们可能通过更多的置换得到 更少的花费呢.

  我们看这组数据:

  ( 1 , 8 , 9, 7 , 6 )   

  离散化后对应为    ,   第一列为标准序列,第二列为当前序列. 

  其循环因子为 ( 1 ) * ( 2 , 4, 3, 5 )  

  对于 循环因子 ( 2, 4 , 3, 5 )  最小置换花费为   dep[2]*3 + ( dep[3]+dep[4]+dep[5] )  = 42

  如果我们首先 把 1, 2 置换.  然后 对于 ( 1, 4, 3, 5 ) 进行置换, 之后再将 ( 1, 2 ) 置换回来总花费为  dep[1]*3 + ( dep[3]+dep[4]+dep[5] ) + 2*(dep[1]+dep[2) = 41

  根据结果我们发现这样我们可以得到一个更优的解, 那么我们需要的结果就是当前 循环因子的最小值 与 整个置换群的 最小值间的对比了

  假设当前循环因子最小dep值的下标为 p, 阶数为 n,  则 当前循环因子最优结果为

    ans =  Min (  2*( dep[1]+dep[p]  )  + dep[1]*(n-1)  ,  dep[p]*(n-1) )     +  sum( 当前循环因子愤怒值和 ) - dep[p] 

  将所有循环因子最优和累加 即为最终答案

解题代码

View Code
#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<algorithm>

using namespace std;

const int inf = 0x3fffffff;

const int N = 1e5+7;

typedef long long LL;

#define MIN(a,b) (a)<(b)?(a):(b)



int a[N], t[N], dep[N], n;

int sum, cnt, Min;

bool vis[N]; //用于标记位置

struct node{

    int x, id;

} p[N];



bool cmp_x( node a, node b)

{

    return a.x < b.x;

}

bool cmp_id(node a,node b)

{

    return a.id < b.id;

}

int check( int s ){

    sum = 0; cnt = 0; Min = inf;

    for(int i = s; i < n; i++)

        if( !vis[i] ) return i;

    return -1;

}

int main(){

    while( scanf("%d", &n) != EOF)

    {

        for(int i = 0; i < n; i++)

        {

            scanf("%d", &( p[i].x ) );

            p[i].id = i;

        }

        sort( p, p+n, cmp_x );    

        for(int i = 0; i < n; i++)

        {    

            a[ p[i].id ] = i; // 在 id 位置上的牛 编号为 i          

            dep[i] = p[i].x;  // 编号为i的牛,愤怒值    

        }        

        sort( p, p+n, cmp_id );    

        int pos = 0;

        LL ans = 0, tmp = dep[0];



        memset( vis, 0, sizeof(vis));    

        while( ( pos = check( pos ) ) != -1 ){

            int s = pos; // s位置    

            while( !vis[s] ){

                vis[s] = true;

                cnt++; sum += dep[ a[s] ]; //记录下s位置下 坐的编号为 a[s]的牛

                Min = MIN( Min, dep[ a[s] ] );

                s = a[s]; //查找 a[s]位置    

            }    

            if( cnt > 1 ){ //当前循环因子数量为cnt,其最小愤怒

                LL t1 =    1LL*(sum-Min)+1LL*Min*(cnt-1);

                LL t2 = 1LL*(sum-Min)+ 1LL*tmp*(cnt-1)+ 2*(tmp+Min);    

                ans += MIN( t1,t2 );    

            }        

            pos++;    

        }

        printf("%lld\n", ans);    

    }

    return 0;    

}

 

 

 

你可能感兴趣的:(sort)