标签:
题意:
给定一个1~n的排列(n<=3000),输出字典序最小且次数最少的交换操作,使得操作后的排列可以通过最少m次交换得到排列[1,2,...n]
Solution:
可以将排列的对应关系看做边,f[i]=i,代表自环。那么根据置换原理,图中有k个环,则需要最少n-k次交换操作得到排列[1,2...n]。所以,先找出图中的环,对同一个环的位置进行标记。这样对不在同一个环的两个位置进行交换,会将两个环合并。将在同一个环内的两个位置进行交换,会将这个环分成两个环。
只需要,判断需要加环还是去环。贪心选择序号较小的位置即可。
/* 置换群 */ #include <bits/stdc++.h> using namespace std; const int MAXN = 3009; int f[MAXN], g[MAXN], pos[MAXN]; int n, m, ans, sum, t; int main() { scanf ("%d", &n); for (int i = 1; i <= n; i++) scanf ("%d", &g[i]), pos[g[i]] = i; scanf ("%d", &m); for (int i = 1; i <= n; i++) if (!f[i]) { f[i] = ++sum; for (int x = i; !f[g[x]]; x = g[x]) f[g[x]] = f[i]; } t = n - sum; printf ("%d\n", m - t > 0 ? m - t : t - m); for (int i = 2; t < m; i++) if (f[1] != f[i]) { t++; for (int x = i; f[x] != f[1]; x = g[x]) f[x] = f[1]; printf ("1 %d ", i); } for (int i = 1; t > m; i++) { if (g[i] != i) for (int j = i + 1; j <= n && t > m; j++) if (f[i] == f[j]) { printf ("%d %d ", i, j); swap (g[i], g[j]); t--; if (g[i] == i) { f[i] = -1; break; } else f[i] = f[g[i]]; if (g[j] == j) f[j] = -1; else { f[j] = ++sum; for (int x = j; f[g[x]] != sum; x = g[x]) f[g[x]] = sum; } } } return 0; }
Codeforces 441D Valera and Swaps(置换群)
标签:
原文地址:http://www.cnblogs.com/keam37/p/4388654.html