【解题报告】POJ 3270 Cow 置换群基础 -- 轮换

写这么多,不知道有没有理解错。
/*
	POJ 3270 Cow 置换群基础 -- 轮换
	题意:
	给一个序列 A[1 8 9 7 6] 仅允许一次交换两个元素,交换代价为两个数字之和
	求最小的代价和使得序列有序(递增)
	元素不会重复
	方法:
	我们很容易算出 排序后的结果为A'[1 6 7 8 9] 也就是让A序列通过交换变为A'序列,
	不过我们在这里尝试反过来考虑,将A'变为A
	A' [1 6 7 8 9]
	A  [1 8 9 7 6]
	对 A 每一列单独分析,
	仅第一列:1不变 (暂且标记为1->1)
	仅第二列:要由6变为8 将 swap(A'(6),A'(8)) 标记为6->8
	仅第三列:要由7变为9 将 swap(A'(7),A'(9)) 标记为7->9
	仅第四列:要由8变为7 将 swap(A'(8),A'(7)) 标记为8->7
	仅第五列:要由9变为6 将 swap(A'(9),A'(6)) 标记为9->6
	我们突然发现最后的标记可以连接成一条 6->8->7->9  (这个[6 ,8 ,7 ,9]序列 called 循环||轮换||群)
	直接给出结论,如果我们要交换次数最少,那么交换一定在一个轮换中进行(模拟下会发现如果不这样,还有可能将有序的打乱去交换)
	对于这个题目,不妨贪心一下,在一个轮换中每次选取最小的元素进行交换,使得轮换有序。(Ⅰ)
	但这样并不一定是最小代价
	因为存在上面的例子([1 6 7 8 9])
	我们还有可能每次选择整个序列中的最小值去直接交换(Ⅱ)(因为最小值可能很小,而其他的都很大)。
	最后只需要在ⅠⅡ之间取最大值即可
	题目至此得解
	符号解释: swap(A'(6),A'(8)) A'序列中值为6的元素与A'序列中值为8的元素交换
*/

#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <ctime>
#include <cmath>
#define CLR(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
T Min(T a,T b){
	return (a>b)?(b):(a);
}

const int N   = 1e4 + 10;
const int INF = (1<<30);

int a[N] , b[N] , id[N*10];
bool vis[N];

int n;
vector <int > store[N]; // 储存循环节
void GetCircel(int x , int cnt){ // 求循环节 
	if(vis[x])return;
	store[cnt].push_back(a[x]);
	vis[x] = 1;
	GetCircel(id[b[x]] , cnt);
}

int main(){
	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	while(cin >> n){
		int mmin = INF;  // 全局最小值
		for(int i = 1 ; i <= n ; i++){
			scanf("%d", &a[i]);
			b[i]=a[i];
			mmin = Min(mmin,a[i]);
		}
		memset(vis , 0 , sizeof(vis));
		sort(a+1 , a+n+1);
		for(int i = 1 ; i <= n ; i++){
			id[a[i]] = i;
		}
		int cnt = 0 , ans = 0;
		for(int i = 1 ; i <= n ; i++){
			if(!vis[i])
				GetCircel(i , cnt++);
		}
		// 输出循环节
		//for(int i = 0 ; i < n ; i++){
		//	if(!store[i].size())continue;
		//	for(int j = 0 ; j < store[i].size() ; j++){
		//		cout << store[i][j] <<" ";
		//	}cout << endl;
		//}
		for(int i = 0 ; i < cnt ; store[i++].clear() ){
			int size = store[i].size() , sum = 0;
			if(store[i].size()<=1)continue;
			int min = store[i][0];   // 循环节中最小值
			for(int j = 0 ; j < size ; j++){
				sum += store[i][j];
				min = Min(min , store[i][j]);
			}
			ans += sum + Min(min*(size-2) , mmin*(size+1) + min);
		}
		cout << ans << endl;
	}
	return 0;
}


/*
in
5
1 8 9 7 6
6
3 4 5 2 1 6

out
41 
16

*/


你可能感兴趣的:(置换群)