杂题 手套

问题描述】
你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。
【输入】
输入第一行一个N,表示手套对数。
第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。
【输出】
一行,包含一个数,表示最少交换次数。
【输入输出样例】

gloves.in

gloves.out

2
0 1 0 1

1

【输入输出样例说明】
将中间两个手套交换过来,颜色序列变成0 0 1 1。
【数据规模与约定】
30%的数据N≤9;
60%的数据N≤1000;
100%的数据N≤200,000。


本文来自 KsCla 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/kscla/article/details/54633416?utm_source=copy

这东西看着复杂,其实就是个逆序对。
它为什么是个逆序对呢?
我们的目的是要把所有颜色相同的手套放在一起,那么考虑暴力的去做,就是把靠后的颜色相同的手套移到靠前的后面,这样一定是最优的。那么这就类似于冒泡排序,冒泡排序的交换次数=逆序对数。
那我们就把每种颜色的手套重编号,编号原则是第一次出现这种颜色的时间,那么每次交换(排序),都会把后面的手套挪到它应该出现的位置,那么这个就是以出现时间为原则的逆序对了。然后直接套上模板,AC!

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a[200004<<1],fu[200004<<1],n,b[200004<<1],q;
bool vis[200004<<1];
long long ans;
void merges(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	merges(1,mid);
	merges(mid+1,r);
	int k=l,i=l,j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<=a[j])
		{
			fu[k++]=a[i++];
		}
		else
		{
			fu[k++]=a[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid)
	{
		fu[k++]=a[i++];
	}
	while(j<=r)
	{
		fu[k++]=a[j++];
	}
	for(int ii=l;ii<=r;ii++)
	{
		a[ii]=fu[ii];
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=2*n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=2*n;i++)
	{
		if(!vis[a[i]])
		{
			vis[a[i]]=true;
			b[a[i]]=q++;
		}
	}
	for(int i=1;i<=2*n;i++)
	{
		a[i]=b[a[i]];
	}
	merges(1,2*n);
	cout<<ans;
}```

猜你喜欢

转载自blog.csdn.net/qq_37073764/article/details/82934411