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

来源:互联网 发布:热血传奇装备数据库 编辑:程序博客网 时间:2024/05/21 00:48
写这么多,不知道有没有理解错。
/*POJ 3270 Cow 置换群基础 -- 轮换题意:给一个序列 A[1 8 9 7 6] 仅允许一次交换两个元素,交换代价为两个数字之和求最小的代价和使得序列有序(递增)元素不会重复方法:我们很容易算出 排序后的结果为A'[1 6 7 8 9] 也就是让A序列通过交换变为A'序列,不过我们在这里尝试反过来考虑,将A'变为AA' [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;}/*in51 8 9 7 663 4 5 2 1 6out41 16*/


原创粉丝点击