数学:置换群-轮换计数(1)

BZOJ1697:置换群的轮换计数

把牛排序,只能每次交换两个牛的位置,代价为这两个牛的脾气之和

问最小交换代价

交换的原始序列是输入序列,目标序列是有序的序列

我们求出每一个轮换,记录每一个轮换的信息,考虑两种情况然后计数就可以了

 1 //对于其中一个循环
 2 //sum + (len – 2) * min 
 3 //sum为这个循环所有数字的和,len为长度,min为这个环里面最小的数字
 4 
 5 //sum + min + (len + 1) * smallest
 6 //sum为这个循环所有数字的和,len为长度,min为这个环里面最小的数字,smallest是整个数列最小的数字
 7 #include<cstdio>
 8 #include<algorithm>
 9 #include<cstring>
10 #define INF 0x7f7f7f7f
11 using namespace std;
12 const int maxn=10005;
13 int n,vmin,cnt,ans;
14 int v[maxn],dis[maxn],vis[maxn],len[maxn],sum[maxn],id[maxn],mn[maxn];
15 int read()
16 {
17     int x=0,f=1;char ch=getchar();
18     while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
19     while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
20     return x*f;
21 }
22 int find(int x)  //有序,二分查找x的最终位置 
23 {
24     int l=1,r=n;
25     while(l<=r)
26     {
27         int mid=(l+r)>>1;
28         if(dis[mid]>x) r=mid-1;
29         else if(dis[mid]==x) return mid;
30         else l=mid+1;
31     }
32     return -1;
33 }
34 void solve(int x)  //处理轮换 
35 {
36     vis[x]=1;cnt++;
37     len[cnt]=1;sum[cnt]+=v[x];mn[cnt]=min(mn[cnt],v[x]); 
38     int now=x;
39     while(v[id[now]]!=v[x])
40     {
41         now=id[now];vis[now]=1;
42         len[cnt]++;sum[cnt]+=v[now];mn[cnt]=min(mn[cnt],v[now]);
43     }
44 }
45 void init()
46 {
47     vmin=INF;
48     memset(mn,127/3,sizeof(mn));
49 }
50 int main()
51 {
52     init();
53     n=read();
54     for(int i=1;i<=n;i++)
55     {
56         v[i]=read();
57         dis[i]=v[i];
58         vmin=min(vmin,v[i]);
59     }
60     sort(dis+1,dis+n+1);
61     for(int i=1;i<=n;i++) id[i]=find(v[i]);  //id每一个元素的最终位置 
62     for(int i=1;i<=n;i++)
63     if(!vis[i]&&i!=id[i]) solve(i);
64     for(int i=1;i<=cnt;i++)
65     {
66         int t1=(len[i]-2)*mn[i];
67         int t2=mn[i]+(len[i]+1)*vmin;
68         ans+=sum[i]+min(t1,t2);
69     } 
70     printf("%d",ans);
71     return 0;
72 }

 

posted @ 2018-09-24 16:41  静听风吟。  阅读(772)  评论(0编辑  收藏  举报