poj3270--Cow Sorting(置换群)

题目链接:点击打开链接

题目大意:给出你n个数,要求把这n个数排列成有序的(由小到大),每次可以交换两个数,花费是这两个数的和,现在求最小的花费

置换群的入门

求出每一个轮换的圈,对于每一个轮换中,只有在自身内交换就能完成有序,而不需要和其它轮换交叉。

一个轮换的最小值temp,轮换中有num个数,轮换的总和是sum,整个序列的最小值min1

让一个轮换花费最少有两种可能

1、轮换自身交换,轮换的最小值和其他值交换num-1次,那么花费是sum-temp+(num-1)*temp

2、用全局的最小值和轮换的最小值交换,然后执行轮换自身交换,然后再用全局最小值把轮换的最小值交换回来

sum-temp+(num-1)*min1+2*(min1+temp)

#include 
#include 
#include 
using namespace std ;
#define LL __int64
#define INF 0x3f3f3f3f
LL a[10010] , b[10010] , id[100010] ;
int vis[10010] ;
int main() {
    int n , i , j ;
    LL ans = 0 , min1 , num , sum , temp ;
    memset(vis,0,sizeof(vis)) ;
    scanf("%d", &n) ;
    for(i = 1 , min1 = INF ; i <= n ; i++) {
        scanf("%d", &a[i]) ;
        b[i] = a[i] ;
        id[ a[i] ] = i ;
        min1 = min(min1,a[i]) ;
    }
    sort(b+1,b+n+1) ;
    for(i = 1 ; i <= n ; i++) {
        if( vis[i] || a[i] == b[i] ) continue ;
        j = i ;
        sum = num = 0 ;
        temp = INF ;
        while( !vis[ id[ b[j] ] ] ) {
            j = id[ b[j] ] ;
            sum += a[j] ;
            num++ ;
            vis[ j ] = 1 ;
            temp = min(temp,a[j]) ;
        }
        ans += min( sum-temp+(num-1)*temp , sum-temp+(num-1)*min1+2*(min1+temp) ) ;
    }
    printf("%I64d\n", ans) ;
    return 0 ;
}



你可能感兴趣的:(数学)