题目描述

小C有一个N个数的整数序列,这个序列的中的数两两不同。小C每次可以交换序列中的任意两个数,代价为这两个数之和。小C希望将整个序列升序排序,问小C需要的最小代价是多少?

输入输出格式

输入格式:

第一行,一个整数N。

第二行,N个整数,表示小C的序列。

输出格式:

一行,一个整数,表示小C需要的最小代价。

输入输出样例

输入样例#1:

6
8 4 5 3 2 7
输出样例#1:

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=;
il ll gi(){
ll a=;char x=getchar();bool f=;
while((x<''||x>'')&&x!='-')x=getchar();
if(x=='-')x=getchar(),f=;
while(x>=''&&x<='')a=a*+x-,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=;i<=n;i++)a[i].v=gi(),a[i].id=i,b[i]=a[i].v,minn=min(minn,a[i].v);
sort(a+,a+n+);
for(int i=;i<=n;i++)
if(a[i].id!=i){
ll s=a[i].id,tot=b[i],l=,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-);
else ans+=min(minn*(l+)+mn+tot,tot+mn*(l-));
}
cout<<ans;
return ;
}

最新文章

  1. ReactiveCocoa源码拆分解析(七)
  2. Send SqlParameter to Dapper
  3. js简单实现div宽度匀速增加/减小
  4. list和set的区别
  5. 这个帖子要收藏,以后用得着--python 实时获取子进程输出
  6. 递归---NYOJ-176 整数划分(二)和NYOJ-279队花的烦恼二
  7. AdHoc发布时出现重复Provisioning Profile的解决方案
  8. 获取Enum枚举值描述的几法方法
  9. 一天搞定CSS---终篇CSS总结
  10. Python Nose框架编写测试用例方法
  11. MYSQL数据库增量备份
  12. c/c++ 重载运算符 基本概念
  13. 【清北学堂2018-刷题冲刺】Contest 5
  14. for循环将字典添加到列表中出现覆盖前面数据的问题
  15. OpenGL实现通用GPU计算概述
  16. unity---gameScreen 的Stats参数
  17. Unity3D-实现连续点击两次返回键退出游戏(安卓/IOS)
  18. 笨办法学Python - 习题3: Numbers and Math
  19. 补发9.28“天天向上”团队Scrum站立会议
  20. 利用Metrics+influxdb+grafana构建监控平台

热门文章

  1. 添加fileinfo扩展
  2. xpath技术解析xm文件(php)
  3. 理解HBase
  4. python-映射&#183;字典
  5. 配置vue-yarm-PM2工具环境
  6. 43-Identity MVC:UI
  7. HBase java API 的使用范例(增,删,查,扫描)
  8. 【TRICK】[0,n)中所有大小为k的子集的方法
  9. Python的re模块的常用方法
  10. iOS中的数据库应用