P2127 序列排序
题目描述
小C有一个N个数的整数序列,这个序列的中的数两两不同。小C每次可以交换序列中的任意两个数,代价为这两个数之和。小C希望将整个序列升序排序,问小C需要的最小代价是多少?
输入输出格式
输入格式:第一行,一个整数N。
第二行,N个整数,表示小C的序列。
输出格式:一行,一个整数,表示小C需要的最小代价。
输入输出样例
6
8 4 5 3 2 7
34
说明
数据范围:
对于30%的数据,1<=N<=10;
对于全部的数据,1<=N<=100000,输入数据中的其他整数均为正整数且不超过$10^9$。
Solution:
本题貌似说是什么群论(可惜至少我现在不会,挖坑拉~手动滑稽~)。但是实际上,一点简单的思维(类似贪心)就可以做出来。
首先我们假设原数列为数组$a$,目标数列为数组$b$($b$为$a$从小到大排序后的数组)。
对比$a_i,b_i$,则我们容易发现,相互之间需要交换位置的数会出现在同一集合中(举样例:$8\;4\;5\;3\;2\;7$排序后为$2\;3\;4\;5\;7\;8$,此时整个数列分为了$2$个集合:$\begin{Bmatrix} 2\;7\;8\end{Bmatrix}$和$\begin{Bmatrix} 3\;4\;5\end{Bmatrix}$)。
容易发现:当某一集合元素大于$size\geq 1$时,该集合内的数一定位置互异,且只需以其中$1$个数去和该集合中其他的$size-1$个数各交换一次,就能使得该集合元素大小和位置对应(可以用样例试试,我这里不模拟了)。
再由题意中交换一次的代价的定义,很容易贪心想到:
1、若只用同一集合中的元素交换,那么使得某一集合中的元素大小和位置对应需要的代价最小应该是用该集合中的最小元素$minn$去和其它元素交换,此时不妨假设某一集合$p$有$k$个元素,这$k$个元素之和为$sum$,设$p$中最小的元素为$p_1$,那么交换代价$cost=(p_1+p_2)+(p_1+p_3)+(p_1+p_4)+…+(p_1+p_{k-1})$,化简得$cost=(k-1)*p_1+sum-p_1=(k-2)*p_1+sum$。可以证明当该交换集合中的最小元素$p_1$同时是全集(即原数列$a$)中的最小值时,上述方法所求的$cost$即为该集合的最小代价。
2、但是当$p$集合中的最小值不是$a$中最小值时,只用同一集合中的元素去交换不一定最优,因为我们可以将全集$a$中的最小值加入$p$集合可能会使$cost$更小(举例:原序列$a:\;1\;15\;11\;13\;14$,若按直接按上面的方法则花费为$75$,但实际上我们将$1$先和$11$进行一次交换代价为$1+11$,这样$1$就以当前最小的代价$12$加进了该交换集合,此时再去算答案为$69$)。由上面的例子容易想到,我们以最小的代价即将$minn$与$p_1$交换,此时$minn$加进了交换集合,则交换代价$cost=minn+p_1+(minn+p_1)+(minn+p_2)+…+(minn+p_k)=minn*(k+1)+tot+p_1$。此时比较一下两种情况,取最小代价。
实现时,就按上述步骤,求交换集合,比较$ans$本题就$ok$了。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long #define inf 233333333333333 using namespace std; const int N=100005; il ll gi(){ ll a=0;char x=getchar();bool f=0; while((x<'0'||x>'9')&&x!='-')x=getchar(); if(x=='-')x=getchar(),f=1; while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); return f?-a:a; } ll n,ans,b[N],minn=inf; struct point{ ll v,id; bool operator <(point a){return v<a.v;} }a[N]; bool vis[N]; int main() { n=gi(); for(int i=1;i<=n;i++)a[i].v=gi(),a[i].id=i,b[i]=a[i].v,minn=min(minn,a[i].v); sort(a+1,a+n+1); for(int i=1;i<=n;i++) if(a[i].id!=i){ ll s=a[i].id,tot=b[i],l=1,mn=b[i]; while(s!=i){ l++; mn=min(mn,b[s]); tot+=b[s]; swap(a[s].id,s); } if(mn==minn)ans+=tot+mn*(l-2); else ans+=min(minn*(l+1)+mn+tot,tot+mn*(l-2)); } cout<<ans; return 0; }