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).

解题思路

  比较有意思的一道题。首先目标序列向原序列对应连边一定会形成若干个环,所以答案其实就是每个环造成的贡献。贪心的想,一个环的贡献首先可以是这个环内最小的元素倒着换一圈,还可以是环内最小的元素先跟全局最小元素换一次,然后让全局最小元素换一圈,然后再换回来(这两种需要画图体会),所以我们每个环在这两种里面取个\(min\)即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>

using namespace std;
const int MAXN = 10005;
typedef long long LL;

inline int rd(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return f?x:-x;	
}

int n,f[MAXN],col,Min,val[MAXN];
LL ans;
bool vis[MAXN];

struct Data{
	int id,a;	
	friend bool operator<(const Data A,const Data B) {
		return A.a<B.a;	
	}
}data[MAXN];

inline int min(int x,int y){
	return x<y?x:y;	
}

int main(){
	n=rd();int now,cnt,x;
	for(int i=1;i<=n;i++) data[i].id=i,data[i].a=rd();sort(data+1,data+1+n);
	for(int i=1;i<=n;i++)
		f[data[i].id]=i,val[data[i].id]=data[i].a;
	for(int i=1;i<=n;i++){
		if(vis[i] || f[i]==i) continue;
		Min=1e9;now=i;cnt=0;
		while(!vis[now]) {
			Min=min(Min,val[now]);vis[now]=1;
			ans+=val[now];now=f[now];cnt++;
		}
		ans+=min((cnt-2)*Min,(Min+(cnt+1)*data[1].a));
	}
	printf("%lld\n",ans);
	return 0;	
}
posted @ 2018-12-03 15:39  Monster_Qi  阅读(228)  评论(0编辑  收藏  举报