我的第一道置换群论题目。
开始的时候不知道这就是置换群,于是对着自己数据各种思考,居然想出来了标准算法的关键部分。
当时的想法是这样的:
从后向前扫描,如果这个数字没有在该在的位置上,那么就用最小的数与它交换,如果最小的数已经在了正确位置上,那么就考虑用次小数与它交换的情况。
但这样的话,如果最小数和次小数都已经到了正确位置上呢?
后来查找题解,才发现可以这样来做:
从前向后扫描,哪个数没有在它该在的位置上,就找出它所在的群。
通过下面这张图可以简单的看出群的定义:
简单的说,一个群就是群内成员互相占领了其他人的位置,适当调换它们之间的位置可以让它们完全归位。
把这个群全部归位的最小代价为Min(用群内最小数为介质与所有群内数交换的代价,先将群内最小数和群外最小数交换、以群外最小数做介质交换完毕再换回群内最小数的代价)
代码:
program csort;//By_Thispoet const maxn=10005; var i,k,p,n,ans :longint; a,b,rank :array[0..maxn]of longint; flag :array[0..maxn]of boolean; function min(i,j:longint):longint; begin if i<j then exit(i);exit(j); end; procedure swap(var i,j:longint); begin if i<>j then begin i:=i xor j;j:=i xor j;i:=i xor j; end; end; procedure qsort(l,r:longint); var i,j,k:longint; begin i:=l;j:=r;k:=b[i+random(j-i+1)]; repeat while b[i]<k do inc(i); while b[j]>k do dec(j); if i<=j then begin swap(b[i],b[j]); inc(i);dec(j); end; until i>j; if l<j then qsort(l,j); if i<r then qsort(i,r); end; function dichotomy(i:longint):longint; var l,r,mid:longint; begin l:=0;r:=n; while l<=r do begin mid:=(l+r)>>1; if b[mid]<=i then begin dichotomy:=mid; l:=mid+1; end else r:=mid-1; end; end; function group(i:longint):longint; begin group:=0; while not flag[i] do begin inc(p); inc(group,a[i]); flag[i]:=true; i:=rank[i]; end; end; begin randomize; readln(n); for i:=1 to n do begin read(a[i]); b[i]:=a[i]; end; qsort(1,n); fillchar(flag,sizeof(flag),0); for i:=1 to n do rank[i]:=dichotomy(a[i]); ans:=0; for i:=1 to n do if not flag[i] then begin p:=0; k:=group(i); inc(ans,min(k+b[i]*(p-2),k+b[i]+(p+1)*b[1])); end; writeln(ans); end.