BZOJ_1697_[Usaco2007 Feb]Cow Sorting牛排序_贪心

时间:2022-09-23 17:15:34

BZOJ_1697_[Usaco2007 Feb]Cow Sorting牛排序_贪心

Description

农夫JOHN准备把他的 N(1 <= N <= 10,000)头牛排队以便于行动。因为脾气大的牛有可能会捣乱,JOHN想把牛按脾气的大小排序。每一头牛的脾气都是一个在1到100,000之间的整数并且没有两头牛的脾气值相同。在排序过程中,JOHN 可以交换任意两头牛的位置。因为脾气大的牛不好移动,JOHN需要X+Y秒来交换脾气值为X和Y的两头牛。 请帮JOHN计算把所有牛排好序的最短时间。

Input

第1行: 一个数, N。

第2~N+1行: 每行一个数,第i+1行是第i头牛的脾气值。

Output

第1行: 一个数,把所有牛排好序的最短时间。

Sample Input

3
2
3
1

输入解释:

队列里有三头牛,脾气分别为 2,3, 1。

Sample Output

7

输出解释:
2 3 1 : 初始序列
2 1 3 : 交换脾气为3和1的牛(时间=1+3=4).
1 2 3 : 交换脾气为1和2的牛(时间=2+1=3).


可以发现每头牛都有一个应该去的位置,假设把每个牛向应该去的位置连边,会出现许多个环,每个环里的牛分别自身交换。

交换的方式有两种:1.每个环里最小的那头牛让环里的其他牛都和他交换一次。2.可能整个序列中的最小值比环里最小的要小很多,

那我们先让他俩交换,再用序列中的最小值和环里其他的牛交换一次,然后再交换回来。

处理出每个环里牛的个数,权值和,最小权值。统计即可。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define N 10050
typedef long long ll;
ll ans;
int siz[N],sol[100050],sum[N],n,a[N],b[N],pos[100050],real_pos[100050],scc,mn[N];
void get_pre() {
int i,j;
for(i=1;i<=n;i++) {
j=i;
if(sol[b[j]]) continue;
sol[b[j]]=1; ++scc;
sum[scc]=mn[scc]=b[j]; siz[scc]=1;
while(1) {
j=pos[b[j]];
if(sol[b[j]]) break;
siz[scc]++;
sum[scc]+=b[j];
mn[scc]=min(mn[scc],b[j]);
sol[b[j]]=1;
}
}
}
int main() {
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) {
scanf("%d",&a[i]);
b[i]=a[i];
real_pos[a[i]]=i;
}
sort(a+1,a+n+1);
for(i=1;i<=n;i++) pos[a[i]]=i;
get_pre();
for(i=1;i<=scc;i++) {
if(siz[i]==1) continue;
ll tmp=min(1ll*(siz[i]-2)*mn[i],1ll*(siz[i]+1)*a[1]+mn[i])+sum[i];
ans+=tmp;
}
printf("%lld\n",ans);
}