置换环理论
以本题为例子
原序列:2 3 4 1 6 5
排序后:1 2 3 4 5 6
这个序列可以找到两个单独的置换环{ {1, 2, 3, 4} {5, 6} },就是几个数通过几次交换到达排好序正确的位置不需要与其他数交换。显然这样是最优的,可以发现,若一个置换环中有n个元素,则可以通过n-1次交换使得这n个元素归位。那么总的交换次数就是置换环的数量-1。
结论是:最少交换次数就是总元素的个数-置换环的数量。
至于证明嘛,这样显然是最优的。因为每个置换环相互独立,互不干扰,而每一个置换环所需最少交换次数又是元素个数-1(这也是显然的),不可能再少了,所以这样就是最少的。
那么我们再来看一下本题
假设置换环长度为n,很显然对于每个置换环要移动某个数字(n-1)次才可以让置换环中所有数字有序,所以一定是移动数值最小的那个吗? 但是不一定,因为你可以先将那个最小的数和整个序列中最小的数交换,排完序后再换回来,这样有可能更优,所以对于每个置换环需要判断一下。
ans+=min(s[i].val*(cnt-1)+sum,s[1].val*(cnt-1)+sum+2*(s[1].val+s[i].val));
这里的s[1].val就是数值最小的那个,2*(s[1].val+s[i].val)就是要先交换s[i]和最小的s[1],操作完再交换回来,所以时间是val×2。
sum是移动所用的时间和,还要减去s[i].val。
s[i]一共移动了cnt-1次,所以是s[i].val*(cnt-1)+sum。
上代码啦
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=10007;
int n;
struct cow
{
int ld,val;
}s[maxn];
int vis[maxn];
int cmp(cow a,cow b)//按val排序
{
if(a.val==b.val)
{
return a.ld<b.ld;
}
else return a.val<b.val;
}
int main()
{
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i].val);
s[i].ld=i;
}
sort(s+1,s+1+n,cmp);//数组的下标为按val大小排序过的顺序,ld记录原下标
int cnt,sum,temp;
for(int i=1;i<=n;i++)
{
if(vis[i])
continue;
temp=i;
cnt=sum=0;//sum记录需要的时间,cnt记录交换的个数
while(vis[temp]==0)
{
sum+=s[temp].val;
cnt++;
vis[temp]=1;
temp=s[temp].ld;
}
sum-=s[i].val;//
ans+=min(s[i].val*(cnt-1)+sum,s[1].val*(cnt-1)+sum+2*(s[1].val+s[i].val));
}
printf("%d",ans);
return 0;
}