交换得来的有序序列

这类题型,一般有两种,一种是交换相邻元素,一种是交换任意元素。

交换相邻的最少次数:

对于从小到大的每个数,先从第一个的逆序对算起,然后把它往前放到第一个,再到第二个,再继续往最前放,也是加上逆序对。

最后其实就等于加上每个数原本的逆序对,因为从大到小的操作,能保证我移动过后,这个元素放到前面也不会增加我其他的逆序对。

交换任意的最少次数:

对于一系列元素,把元素和其有序时候的位置建立边(位置->位置),即可以发现:

举例:3 1 2 5 4

位置1(3)->位置3(2)->位置2(1)->位置1。是一个循环节,也是一个有向环。

对于这个环:我们内部排序只需要len(环)-1次即可。

总共次数:len(序列)-num(循环节个数)。

P2127 序列排序

题意:交换任意元素达成有序,每一次操作的贡献是两元素的和,问总贡献最少多少。

求循环节:循环节内部肯定是和最小交换达成结果贡献最少。

但还需要考虑的是:外部最小的交换进来+用外部最小的来交换有序+把最小的换回来的贡献可能会更小。

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<cctype>
#include<string>
#include<queue>
#define rep(a,b) for(register int i=a;i<=b;i++)
#define red(a,b) for(register int i=a;i>=b;i--)
#define  ULL unsigned long long
#define  LL long long
using namespace std;


struct node
{
	LL num;
	int ind;
}arr[100050];
int visit[100050];

int cmp(node a,node b)
{
	return a.num<b.num;
}
int main()
{
   int n;
   cin>>n;
   vector<node>s;
   arr[0].num=-100;
   arr[0].ind=0;
   s.push_back(arr[0]);
   rep(1,n)
   {
      scanf("%lld",&arr[i].num);
      arr[i].ind=i;
      s.push_back(arr[i]);
   }
   sort(s.begin(),s.end(),cmp);
 /*  rep(1,n)
   {
   	  cout<<s[i].num<<" "<<s[i].ind<<endl;
   }
   */
   vector<LL>temp;
   LL sum=0;
   rep(1,n)
   {
   	  if(!visit[i])
      {
      	    temp.clear();
      	    int j=i;
 			while(visit[j]==0)
 			{
 				temp.push_back(s[j].num);
			 	visit[j]=1;
			 	j=s[j].ind;
		    }
		    sort(temp.begin(),temp.end());
		    if(temp.size()>1)
			{
			  LL tot=0;
			  for(int k=0;k<temp.size();k++)
		      {
    			tot+=temp[k];
    		  }
    		  sum+=min(tot+temp[0]+(temp.size()+1)*s[1].num,tot+temp[0]*(temp.size()-2));
			}
      } 
   }
   cout<<sum<<endl; 
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值