poj 3270 Cow Sorting
这个题是一道置换问题的题,是黑书上面的无聊的排序问题;根据群论知识我们可以分解为S个不想交的循环的乘积。例如:8 4 5 3 2 7,目标状态为2 3 4 5 7 8,
我们可以分解为两个循环的乘积,(8 2 7 ), ( 4 3 5 )。我们知道对两个循环分别进行完成交换,就可以完成任务;
对于任意一个循环i,设他的长度为ki,容易证明至少要循环ki - 1次,即每次让一个元素到达目标位置,而当第ki - 1元素到达目标以后显然第ki个也到达目标。我们知道每个循环至少要交换一次,要代价最小,因此我没每次拿最小的元素去交换,总的花费是sum(循环总值)+ ( cnt(循环个数) - 2 )*min(循环中的最小元素);
但是这种方法并不是最优的例如 1 6 7 8 9,可分解(1)(8 6 9 7)我们按上述方法算出的方法是 6 + 7 + 8 + 9 + (4 - 2)*6 = 42;
而实际是最小的值是6 + 7 + 8 + 9 + 6 + (4 + 1 )*1 = 41;
这是因为我们借助的循环以外的数和他们交换,我们让1与6进行交换那么1就进入了循环(6 1 7 8 9 )这样在进行原先的方法进行计算;
cost = min( sum(循环总和) + MIN(循环中最小值)*( cnt(循环个数) - 2 ), sum + Min(全部元素最小值)*(cnt+1) + MIN );
View Code
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<set> #include<map> #include<cstring> #include<vector> #include<string> #define LL long long using namespace std; class Node { public: int num,place; }node[10024]; bool cmp1( Node a , Node b ) { return a.num < b.num; } int Solve( int n ,int Min ) { int sum = 0; bool visit[10024] = {0}; for( int i = 0 ; i < n ; i ++ ) { if( !visit[i] ) { int s = node[i].num,cnt=1,m = node[i].place,MIN = node[i].num ; while( m!= i ) { visit[m] = true; s += node[m].num; cnt++; if( node[m].num < MIN ) MIN = node[m].num; m = node[m].place; } sum += min( s + MIN*( cnt - 2 ), s + Min*(cnt+1) + MIN ); } } return sum; } int main( ) { int n; while( scanf( "%d",&n )==1 ) { int Min = 0x7fffffff; for( int i = 0 ; i < n ; i ++ ) { scanf( "%d",&node[i].num ); node[i].place = i; Min = min( node[i].num , Min ); } sort( node , node + n , cmp1 ); printf( "%d\n",Solve( n , Min ) ); } //system( "pause" ); return 0; }