置换群的基本概念与题目

首先介绍一下什么是置换群,不说一些繁琐的概念. 直接举例
首先给你一个序列,假如:
s = {1 2 3 4 5 6}
然后给你一个变换规则
t = {6 3 4 2 1 5} (即s序列中第一个和第六个换, 第二个和第三个换, 然后一直这样)
这是变换三次的结果 :
第一次:6 3 4 2 1 5
第二次:5 4 2 3 6 1
第三次:1 2 3 4 5 6
可以发现经过几次会变换回去,在变换下去就是循环的了,这就是一个置换群.
我们可以这样表示一个置换群,比如按照上面变化规则
1->6->5->1 那么这些是一个轮换
2->3->4->2 这些是一个轮换
所以可以写为
t = { {1 6 5},{ 2 3 4 }};
下面是一些简单的置换群的题:

序列置换
//这个是裸的置换的题, 可以发现最少需要交换的顺序就是每一个循环节的循环长度的最小公倍数,. 注意res用long long
AC Code

const int maxn=2e2+5;
int nex[maxn];   //下一个要到达的点.
bool vis[maxn];  //标记是否循环过
void solve()
{
    int n;
    while(~scanf("%d",&n)){
        Fill(nex,0); Fill(vis,false);
        for(int i=1;i<=n;i++){
            scanf("%d",&nex[i]);
        }
        ll res = 1;
        for(int i=1;i<=n;i++){
            if(vis[i]) continue;
            int tmp = i;
            ll cnt = 1;  //循环节长度
            vis[tmp] = true;
            while(nex[tmp] != i){
                tmp = nex[tmp];  //访问的数
                vis[tmp] = true;
                cnt++;
            } 
            res = res/__gcd(res,cnt)*cnt;
        }
        printf("%lld\n",res);
    }
}

poj — 3270
//题意: 就是然你交换几次使得原来的序列变得有序(增加).
//思路: 这道题还是要巧妙运用置换群.
首先我们求出所有轮换,对于一个轮换,我们燃心的用其中最小的元素去交换得到其他的显然是最优的方案.
例如, 数字是8 4 5 3 2 7
明显, 目标状态是2 3 4 5 7 8,能写为两个轮换:(8 2 7)(4 3 5).
观察其中一个轮换, 要使交换代价最小, 应该用循环里面最小的数字2,去与另外的两个数字, 7与8交换. 这样交换的代价是 :

sum - tmpmin + (len - 1) * tmpmin

其中, sum为这个循环所有数字的和, len为长度, tmpmin为这个环里面最小的数字 .
考虑到另外一种情况, 我们可以从别的循环里面调一个数字, 进入这个循环之中, 使交换代价更小. 例如初始状态:1 8 9 7 6 可分解为两个循环:(1)(8 6 9 7),明显,第二个循环为(8 6 9 7),最小的数字为6 . 我们可以抽调整个数列最小的数字1进入这个循环。使第二个循环变为:(8 1 9 7)
让这个1完成任务后,再和6交换,让6重新回到循环之后。这样做的代价明显是:

sum + tmpmin+ (len + 1) * min

其中,sum为这个循环所有数字的和,len为长度,tmpmin为这个环里面最小的数字,min是整个数列最小的数字
所以我们每次都去取这两种情况中较小的一种.

AC Code

const int maxn=1e5+5;
int nex[maxn],a[maxn],b[maxn];
bool vis[maxn];
void init()
{
    Fill(nex,0); Fill(vis,false);
    Fill(a,0); Fill(b,0);
}
void solve()
{
    int n;
    while(~scanf("%d",&n)){
        init();
        int maxx = -inf, minn = inf;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i] = a[i];
            maxx = max(maxx,a[i]);
            minn = min(minn,a[i]);
        }
        sort(b+1,b+n+1);
        for(int i=1;i<=n;i++) nex[a[i]] = b[i];   //所给的序列需要到达的一种状态.
        ll res = 0;
        for(int i=1;i<=n;i++){
            if(vis[a[i]]) continue;
            int tmp = a[i];
            ll cnt = 1, sum = 0;
            sum += tmp;
            int tmpmin = tmp;   //取当前这个序列的最小值.
            vis[tmp] = true;
            while(nex[tmp] != a[i]){
                tmp = nex[tmp];
                vis[tmp] = true;
                cnt++; sum += tmp;
                tmpmin = min(tmp,tmpmin);
            }
            //在全局最小值构成的代价和局部最小值构成的代价中选一个小的.
            res += min(sum-tmpmin+(cnt-1)*tmpmin,sum+tmpmin+(cnt+1)*minn);
        }
        printf("%lld\n",res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值