我的第一道置换群论题目。

开始的时候不知道这就是置换群,于是对着自己数据各种思考,居然想出来了标准算法的关键部分。

当时的想法是这样的:

从后向前扫描,如果这个数字没有在该在的位置上,那么就用最小的数与它交换,如果最小的数已经在了正确位置上,那么就考虑用次小数与它交换的情况。

但这样的话,如果最小数和次小数都已经到了正确位置上呢?

后来查找题解,才发现可以这样来做:

从前向后扫描,哪个数没有在它该在的位置上,就找出它所在的群。

通过下面这张图可以简单的看出群的定义:

简单的说,一个群就是群内成员互相占领了其他人的位置,适当调换它们之间的位置可以让它们完全归位。

把这个群全部归位的最小代价为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.