码迷,mamicode.com
首页 > 编程语言 > 详细

CodeVS 2845 排序的代价

时间:2016-09-28 22:52:42      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:

Description

给你一个数列使他递增,交换两个元素的代价为两个数的和,最小化代价. 

Sol

置换群+离散化.

使一个数列恢复递增顺序,那么,他和他要到达的位置的数需要交换,这样就形成了一个置换.

对于一个有向圈的置换,我们可以证明它的最小代价就是这个有向圈中 最小元素*(有向圈的大小-1)+其他数的和-最小元素.

1个大小为n的有向环,至少会进行n-1次交换才能成为n个1元环

当 \(size=1,2\) 时,它显然成立.

当 \(size>2\) 时,每次交换1个元素会将圆圈拆成两个互不相交的置换,一直拆下去需要拆n-1次.

 

然后还有一个问题就是我们可以把一个最小的数拉过来进行交换,这样我们就需要的代价就是,用最小的和有向圈中最小的交换,再让最小的*(有向圈大小-1)

 

这样这道题就显而易见了,至于不是连续的,我们可以离散化.

 

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int N = 1005;

int n,cnt,ans,ca=1;
int r[N],a[N],b[N],c[N],v[N],s1;
int p[N][N],s[N],h[N];

inline int in(int x=0,char ch=getchar()){ while(ch>‘9‘||ch<‘0‘) ch=getchar();
	while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();return x; }
int main(){
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++) r[i]=in();
		cnt=ans=0;
		s1=r[1];for(int i=1;i<=n;i++) if(r[i]<s1) s1=r[i];
		for(int i=1;i<=n;i++) a[i]=r[i];
		sort(a+1,a+n+1);
		
		for(int i=1;i<=n;i++) b[a[i]]=i;
		for(int i=1;i<=n;i++) c[i]=b[r[i]];
		
//		for(int i=1;i<=n;i++) printf("%d%c",a[i]," \n"[i==n]);
//		for(int i=1;i<=n;i++) printf("%d%c",r[i]," \n"[i==n]);
//		for(int i=1;i<=n;i++) printf("%d%c",b[i]," \n"[i==n]);
//		for(int i=1;i<=n;i++) printf("%d%c",c[i]," \n"[i==n]);
		
		memset(v,0,sizeof(v));
		
		for(int i=1,t;i<=n;i++) if(!v[i]){
			t=i,++cnt,p[cnt][0]=0,s[cnt]=r[i],h[cnt]=0;
			while(!v[t]) v[t]=1,p[cnt][++p[cnt][0]]=r[t],s[cnt]=min(s[cnt],r[t]),h[cnt]+=r[t],t=c[t];
		}
		
//		for(int i=1;i<=cnt;i++) for(int j=0;j<=p[i][0];j++) printf("%d%c",p[i][j]," \n"[j==p[i][0]]);
		
		for(int i=1;i<=cnt;i++){
			ans+=min(s[i]*(p[i][0]-1)+h[i]-s[i],s[i]*2+s1*2+s1*(p[i][0]-1)+h[i]-s[i]);
		}
		
		if(!ans) break;
		printf("Case %d: %d\n",ca++,ans);
	}return 0;
}

  

CodeVS 2845 排序的代价

标签:

原文地址:http://www.cnblogs.com/beiyuoi/p/5918058.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!