CodeVS 2845 排序的代价

Posted 北屿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeVS 2845 排序的代价相关的知识,希望对你有一定的参考价值。

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 排序的代价的主要内容,如果未能解决你的问题,请参考以下文章

[codevs1048]石子归并&&[codevs2102][洛谷P1880]石子归并加强版

1048 石子归并codevs

Codevs 2630宝库通道

CODEVS 1048 石子归并 题解

codevs 1048石子归并

codevs 1048 石子归并