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 }