思路:
排序的花销最小,可以先让排序的中交换的次数最小,就是闭环交换。
考虑如何交换使排序得到的序列最小,按照闭环交换元素序列最后肯定可以将元素放到相应的位置上
(闭环交换:num -> pos[num] -> pos[pos[num]] ->……)但是要求交换过程中的元素和(num+pos[num])最小,
可以选取闭环交换过程中的最小元素作为中间值,然后交换相应的节点。
eg:
n = 5
4 3 1 2 5
第一个闭环4 -> 1 -> 3 -> 2 -> 4
以1位媒介交换 (1,3),(1,2),(1,4)得到 1,2,3,4,5序列。
但是这样并不是最终答案,因为闭环中的最小元素不意味着整个序列的最小元素,这样的交换花销为(sum-tp)+tp*(tm-1)
(其中,sum表示闭环内不同元素的和,tp表示闭环中最小的元素,tm表示闭环中的元素出现的次数)。
如果是 2 220 340 789 这个序列,当然是用1作为媒介的花销最小了,这样的最小花销为sum+tp+mi*(tm+1)
(其中,mi表示整个序列最小的元素)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100100;
int a[maxn]={0},nxt[maxn]={0},vis[maxn]={0};
struct Node{
int data,id;
}cur[maxn];
bool cmp(Node a,Node b){
return a.data<b.data;
}
int MIN(int x,int y){
return x<y?x:y;
}
int main(void)
{
int n,i,j,mi=maxn;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",&cur[i].data);
cur[i].id = i;
mi = MIN(mi,cur[i].data);
}
sort(cur,cur+n,cmp);
for(i=0;i<n;i++) nxt[i] = cur[i].id;
int ans = 0;
for(i=0;i<n;i++)
if(vis[i]==0){
int sum = 0,tp = maxn,tim = 0,x = i;
while(vis[x]==0){
vis[x] = 1;
sum += cur[x].data;
tp = MIN(tp,cur[x].data);
tim++;
x = nxt[x];
}
tim--;sum-=tp;
ans += MIN(sum+tp*tim,sum+(tim+2)*mi+tp*2);
}
printf("%d\n",ans);
return 0;
}