wikioi2845 排序的代价 Polay定理

来源:互联网 发布:mac版lol官网 编辑:程序博客网 时间:2024/05/17 09:12

群论题,涨姿势啊涨姿势,牛掰的群论。先确定循环有几个,再按照遍历找出循环,记录每个循环的最小值和所有数的和。

最后判断每个循环节所用的最小代价时有两种情况:

第一种是在当前循环里把最小数作为调动的工具。

第二种是把整个数列里最小的数字和循环里最小数字交换,把数列里最小的数字作为工具,最后两个数要换回来。

#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>using namespace std;int n;int mino;bool labelperm[1010];struct node{    int pos,w;}box[1100];struct loop{    int mina,sum,num,head;}perm[1100];int findpos(){    for(int i=0;i<n;i++)    {        box[i].pos=0;        for(int j=0;j<n;j++)        {            if(box[i].w>box[j].w)            {                box[i].pos++;            }        }    }}void minn(){    mino=0x3f3f3f3f;    for(int i=0;i<n;i++)    {        mino=min(mino,box[i].w);    }}int getans(int k){    if(perm[k].num==1) return 0;    int r1=perm[k].sum+(perm[k].num-2)*perm[k].mina;    int r2=perm[k].sum+(perm[k].num-1)*mino-perm[k].mina+2*(mino+perm[k].mina);    return min(r1,r2);}int countg(){    int sum=0;    for(int i=0;i<n;i++)        labelperm[i]=true;    for(int i=0;i<n;i++)    {        if(labelperm[i])        {            int k=0;        for(k=i;box[k].pos!=i;k=box[k].pos)        {            labelperm[k]=false;        }        labelperm[k]=false;        labelperm[i]=true;        }    }    for(int i=0;i<n;i++)        if(labelperm[i])        {            perm[sum].head=i;            sum++;        }    for(int i=0;i<sum;i++)    {        perm[i].mina=0x3f3f3f3f;        perm[i].sum=perm[i].num=0;        for(int j=perm[i].head;j!=perm[i].head || perm[i].num==0 ;j=box[j].pos)        {            perm[i].mina=min(perm[i].mina,box[j].w);            perm[i].sum+=box[j].w;            perm[i].num++;        }    }    return sum;}int main(){    int tot=0;while(scanf("%d",&n)!=EOF){tot++;for(int i=0;i<n;i++){scanf("%d",&box[i].w);}findpos();int permnum=countg();minn();int ans=0;for(int i=0;i<permnum;i++){ans+=getans(i);}if(ans==0){return 0;}printf("Case %d: %d\n",tot,ans);}    return 0;}


0 0
原创粉丝点击