码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 441D Valera and Swaps(置换群)

时间:2015-04-02 23:49:02      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:

题意:

  给定一个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;
}
View Code

 

Codeforces 441D Valera and Swaps(置换群)

标签:

原文地址:http://www.cnblogs.com/keam37/p/4388654.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!