排序后求交换次数,其实就是求逆序数。由于数据庞大,冒泡解决时间复杂度为O(n*n)会超时。所以用归并排序的方法时间复杂度降为O(n*logn)。
主要难点在于归并排序如何求逆序数, 在每次对2个有序集合进行merge时,当右边集合的elem[j]小于左边集合elem[i]时,则说明elem[j]小于左边集合
elem[i]---elem[mid] mid-i+1个元素的值。则对于这个元素,它的逆序数即为mid-i+1。将所有右边集合元素的逆序数加起来就是总逆序数,再通过递归方法可以
求得总逆序数。(sum应该定义为全局变量,当时做的时候搞麻烦了 。。。O,o)
#include<iostream>
using namespace std;
long long Merge(int temp[],int elem[],int low,int mid,int high)//对于2个有序集合的排序和算逆序数方法
{
int i,j,k;//临时变量,i为elem[low...mid]下标,j为elem[mid+1...high]下标,k为temp[]下标
long long sum=0;//每次归并时右边集合每个元素的逆序数和
for(i=low,j=mid+1,k=low;j<=high&&i<=mid;k++)//分别用2个下标走左右2个集合,小的就放到临时数组中
{
if(elem[i]<elem[j])
{
temp[k]=elem[i];
i++;
}
else//如果右边集合下标为j的元素小一些,则由于2个集合均为有序的。所以j元素就比前面mid-i+1个元素小,即为逆序数
{
temp[k]=elem[j];
j++;
sum+=mid-i+1;//通过递归将所有的逆序数相加
}
}
for(;i<=mid;k++)//将剩余的元素放入临时数组中
{
temp[k]=elem[i++];
}
for(;j<=high;k++)
{
temp[k]=elem[j++];
}
for(i=low;i<=high;i++)//把临时数组拷贝到原数组中
{
elem[i]=temp[i];
}
return sum;
};
long long MergeHelp(int temp[],int elem[],int low,int high)//通过递归方法来排序求sum
{
long long sum=0;
if(low<high)
{
int mid=(low+high)/2;
sum+=MergeHelp(temp,elem,low,mid);//左边集合排序
sum+=MergeHelp(temp,elem,mid+1,high);//右边集合排序
sum+=Merge(temp,elem,low,mid,high);//2个集合排序
}
return sum;
};
int main()
{
int n;
while(cin>>n&&n)
{
long long sum=0;
int *elem=new int[n+2];
int *temp=new int[n+2];
for(int i=0;i<n;i++)
cin>>elem[i];
sum=MergeHelp(temp,elem,0,n-1);
cout<<sum<<endl;
}
return 0;
}