POJ 3270 Cow Sorting (置换群利用) 位置交换问题

题目:有一串数字,要将它排列成升序,每次可以交换两个数,交换一次的代价为两数之和。要求代价最小。所有的数都是唯一的。

将原有数列排序之后,得到目标串,这样就与原串形成了置换。

1.对于单个循环群来说(大小大于1),所有的数的位置与目标位置完全不同,所以最理想的交换的情况是需要交换n-1次。例如串 2 3 4 5 1 变成1 2 3 4 5 这是最简单的循环群,需要交换n-1次并且每个数字都要参与交换,所以对于这个题需要最小的代价的话,只需要每次让该群中最小的与其他值进行交换位置,这个时候最小值会占据另一个值得位置,然后接着交换,此时就完成了最小值与其他值进行了 n-1次交换了。

   所以在一个循环内部的最优解又是用循环中最小的数,依次与其它数进行交换,如果循环节长度为m,那么最小的数需要交换m-1次,而其它数各一次。

2.但是这样的置换中分为多个循环群,

但是这样并不一定最优,因为有一种特殊情况,就是用循环外的一个数,与循环内的所有数交换,利用这个非常小的数进行中介。

那么最终就是有两种情况。一种是用循环内部最小的数为中介,另外一种就是用整体的最小的数为中介交换。

找到每个循环节的长度以及最小元素。


详解见黑书247页。循环群的学习

#include<iostream>  /************黑书247页********/
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
#define INF 0x3f3f3f3f
#define bug puts("************")
using namespace std;
const int N=11100;
struct node{
    int id;
    int val;
    friend bool operator<(node p1,node p2){
        return p1.val<p2.val;
    }
}p[N];
int vis[N];
int main(){
    int n;
    scanf("%d",&n);
    int sum=0;
    int min1=INF;
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i].val);
        min1=min(min1,p[i].val);
        p[i].id=i;
    }
    sort(p+1,p+n+1);
    int cnt=0;
    for(int i=1;i<=n;i++){
        if(i==p[i].id)continue;
        int sum1=0;
        int min2=INF;
        int num=0;
        if(!vis[i]){
            vis[i]=1;
            sum1+=p[i].val;
            min2=min(min2,p[i].val);
            int tmp=p[i].id;
            num++;
            while(!vis[tmp]){
                vis[tmp]=1;
                sum1+=p[tmp].val;
                min2=min(min2,p[tmp].val);
                tmp=p[tmp].id;
                num++;
            }
            sum+=sum1+min(min1*(num+1)+min2,min2*(num-2));
        }
    }
    printf("%d\n",sum);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值