poj 3270 Cow Sorting(置换)

19 篇文章 0 订阅

Cow Sorting
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 6768 Accepted: 2642

Description

Farmer John's N (1 ≤ N ≤ 10,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" level in the range 1...100,000. Since grumpy cows are more likely to damage FJ's milking equipment, FJ would like to reorder the cows in line so they are lined up in increasing order of grumpiness. During this process, the places of any two cows (not necessarily adjacent) can be interchanged. Since grumpy cows are harder to move, it takes FJ a total of X+Y units of time to exchange two cows whose grumpiness levels are X and Y.

Please help FJ calculate the minimal time required to reorder the cows.

Input

Line 1: A single integer:  N
Lines 2.. N+1: Each line contains a single integer: line  i+1 describes the grumpiness of cow  i

Output

Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.

Sample Input

3
2
3
1

Sample Output

7

Hint

2 3 1 : Initial order. 
2 1 3 : After interchanging cows with grumpiness 3 and 1 (time=1+3=4). 
1 2 3 : After interchanging cows with grumpiness 1 and 2 (time=2+1=3).

Source

[Submit]   [Go Back]   [Status]   [Discuss]

题目大意:给出一个数列,请你把序列升序排列,每次可以交换任意的两个数,交换的代价为两个数的和,求最小代价。

题解:置换群。
任一集合A到自身的映射都叫做A的一个变换,如果A是有限集且变换 是一一变换(双射),那么这个变换为A的一个置换。         

有限集合A的若干个置换若作成群,就叫做置换群。含有n个元素的有限群A的全体置换作成的群,叫做n次对称群。通常记为Sn.

例如给出一排数8 4 5 3 2 7,那么我们最终的状态为2 3 4 5 7 8,这里的轮换(也可以说是一个循环节)有(8 2 7)(4 5 3),即对于(8 2 7)这个轮换,我们需要把2换到8的位置,7换到2的位置,8换到7的位置,我们称这是一个轮换。那么对于每个轮换的内部操作我们考虑如何最优呢?因为要使代价最小,那么我们就尽量不移动大的数,移动小的数,每次把轮换中最小的数与轮换中的其他数交换K-1次,就可以得到我们想要的轮换后的序列(其中K为轮换中数的个数),此时的代价为sum-min+(k-1)*min(其中sum为轮换组中的总和,min为轮换中的最小值)

那是否还有更优的操作呢?我们可以找出序列中最小的数,然后与轮换中的最小的数交换,用这个序列中最小的数与轮换中的数进行交换(即代替原来的最小的数进行上述的操作),操作完成后,在将序列中最小的数与轮换中原本最小的数交换回去,即可,这时的代价为sum+min+(k+1)*minnum。

我们只需要每次寻找出轮换,然后从两种方式中选择代价小的进行操作即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 100003
using namespace std;
int n,m;
int a[N],cnt[N],minn,maxn,use[N];
int main()
{
	scanf("%d",&n);
	minn=100000; maxn=0;
	for (int i=1;i<=n;i++)  
	  scanf("%d",&a[i]),
	  minn=min(minn,a[i]),maxn=max(maxn,a[i]),
	  cnt[a[i]]++;
	for (int i=1;i<=maxn;i++)//记录每个值在序列中应处的位置
	 cnt[i]=cnt[i]+cnt[i-1];
	int ans=0;
	for (int i=1;i<=n;i++)
	if (!use[i])
	{
		int j=i;  int len=0; int sum=0; int t=a[i];
		int mnow=1000000;
		while (!use[j]){//确定轮换
			len++; 
			sum+=a[j]; 
			mnow=min(mnow,a[j]);
			use[j]=1;
			j=cnt[a[j]];
		}
		if (len>1)  ans+=sum;
		if (len>=2){
			int t1=-mnow+(len-1)*mnow; int t2=mnow+(len+1)*minn;
			ans+=min(t1,t2);
		}
	}
	printf("%d\n",ans);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值