POJ3270 Cow Sorting 置换群循环节

题目链接:POJ3270

题目大意:给你一组序列,目标为有序序列,每次只能交换两个数,每次的花费为交换的两个值的和,求如何交换使得花费最少。
知识点:置换群循环节

  1. 我们知道,当一个循环节中最小的值与这个循环节中所以其他值交换,花费最少,并且交换完后有序。

  2. 假设循环节长度为k,循环节中最小值为m,那么只需要用最小值m与其他值交换k-1次,即可有序。代价这样算,除最小值m之外的数,只需要一次就可以到自己的有序位置上,所以这些数的代价为 sum-m,而最小值m交换了k-1次,产生了(k-1)*m,代价,所以这种情况的总代价为:sum+(k-2)*m

  3. 在考虑另外一种情况,考虑到整个序列的最小值为minm(此最小值不在当前循环节中),当用更小的值去交换当前循环节中的值,所花费代价更小。怎么考虑代价呢?我们先把minm与m交换,也就是说把minm放进当前循环节,在重复以上操作,代价为:sum-m+minm+(k-1)minm,然后再把minm与m交换,这样一进一出,产生的代价为 2(minm+m)
    所以总的代价为 sum+minm*k+m
    对每个循环节的交换代价取这两种情况的最小值即可,我应该说清楚了吧……

    其他在代码中有注释

/*
2017年8月1日16:45:22
POJ3270
置换群求循环节  
AC代码 
*/
#include
#include
#include
using namespace std;
const int maxn=100000+10;
const int inf=0x3f3f3f3f;
int a[maxn],b[maxn]; 
int n,sum,tot; 
int min_num;//记录序列中最小值 
struct node{
    int min_val;//循环节中最小值 
    int num;//循环节长度 
}c[maxn];
bool vis[maxn];//访问标记 

void dfs(int val){
    for(int i=0;iif(b[i]==val&&!vis[i]){
            vis[i]=true;
            c[tot].num++;
            c[tot].min_val=min(c[tot].min_val,b[i]);
            dfs(a[i]);
        }
    }
}
int main(){
    while(~scanf("%d",&n)){
        sum=0;min_num=inf;
        memset(vis,false,sizeof(vis));
        for(int i=0;iscanf("%d",&a[i]);
            b[i]=a[i];//将a数组复制到b数组 
            min_num=min(min_num,a[i]);//保存序列中最小值 
            sum+=a[i];//求序列和 
        }
        /*对a数组进行排序-即变换的目标序列*/ 
        sort(a,a+n);
        /*循环节个数total*/ 
        tot=0; 
        /*求出所有循环节包括长度和每个循环节中最小值*/ 
        for(int i=0;i/*如果该点访问过<=>该点在另外一个循环节中->跳过*/
            if(vis[i]) continue;
            else{
                /*循环节初始化,最小值初始化为第一个值
                长度初始化为1,该点打标记
                然后进入对应序号的b[i]
                举个例子:b数组:2 3 1
                          a数组:1 2 3
                先访问的是b[0]=2,然后我们在访问 a[0]也就是2对应的1
                DFS搜索,在从b数组中找到等于a[0]的值的下标i,此时
                b[i]=b[2]=1,下标2打标记,我们再访问a[2],如此循环即可
                直到访问到已经访问过的点<=>回到了循环的起始点->结束搜索
                第一个循环节就找到了,应该说明白了吧:)*/
                c[tot].min_val=b[i];
                c[tot].num=1;
                vis[i]=true;
                dfs(a[i]);
                tot++;
            }
        }
        for(int i=0;i2)*c[i].min_val,c[i].min_val+(c[i].num+1)*min_num);
        } 
        printf("%d\n",sum); 
    } 
    return 0;
} 

你可能感兴趣的:(WHU暑假集训,POJ,置换群循环节,POJ3270,置换群循环节)