poj2299 Ultra-QuickSort(逆序对)

题意

给定一个长度为n的序列A,如果只允许进行比较和交换相邻两个数的操作,求至少需要多少次交换才能把A从小到大排序。


题解

归并排序求逆序对
为什么要求逆序对呢?在一个有序的序列中逆序对的个数为0,逆序对就好像一个个反叛军,只要有一个存活,这个序列就会“不得安宁”。消灭所有逆序对就成了本题的关键。
题目很给力,逐个消灭逆序对的方法就是通过把邻近的逆序对交换。所以,只要能求出A中逆序对的个数,本题就引刃而解了。
拿样例举个例吧:
①9 1 0 5 4
9 1\
9 0 \ 消除与9相关的逆序对
9 5 /
9 4/

②1 0 5 4 9
1 0--消除与1相关的逆序对

③0 1 5 4 9
5 4--消除与5相关的逆序对

④0 1 4 5 9


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500010;


int n;long long cnt;
int a[maxn],b[maxn];


void merge(int l,int r)//合并操作
{
	int mid=l+r>>1;
	int i=l,j=mid+1;
	for(int k=l;k<=r;k++)
	{       //主意此处需要i<=mid以防a[i]超过mid后到了a[j]的地盘
		if( j>r || (i<=mid && a[i]<=a[j]) ) b[k]=a[i++];//a[i]<a[j] 非逆序对 将a[i]存入b 
		else cnt+=mid-i+1,b[k]=a[j++];//a[i]>a[j] 逆序对 将a[j]存入b 
	}
	for(int k=l;k<=r;k++) a[k]=b[k];
}


void mergesort(int l,int r)//归并排序
{
	if(l==r) return ;
	int mid=l+r>>1;
	mergesort(l,mid);mergesort(mid+1,r);
	merge(l,r);
}


int main()
{
	while(scanf("%d",&n),n!=0)
	{
	 	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	 	cnt=0;
	 	mergesort(1,n);
	 	printf("%lld\n",cnt);//cnt要开long long(MAXcnt=500000*(500000-1)/2=124999750000)
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/a_bright_ch/article/details/80998842