#include <iostream>
using namespace std;
typedef int ElementType;
long long sum = 0;//逆序数的个数
void MergeSort(int arr[], int tmp[],int L, int rightend);//归并排序
void MergeAndCountNum(int arr[], int tmp[], int L, int R, int rightend); //归并有序序列并计算逆序数的个数
int main()
{
int n;
cin >> n;
ElementType *a = (ElementType *)malloc(n*sizeof(ElementType));
ElementType *tmp = (ElementType *)malloc(n*sizeof(ElementType));
for (int i = 0; i < n; i++)
cin >> a[i];
MergeSort(a, tmp, 0, n - 1);
cout << sum << endl;
free(a);
free(tmp);
return 0;
}
//归并排序
void MergeSort(int a[], int tmp[],int L, int rightend)
{
if (L < rightend)
{
int center = L + (rightend - L) / 2;
MergeSort(a, tmp, L, center);
MergeSort(a, tmp, center+1, rightend);
MergeAndCountNum(a, tmp, L, center + 1, rightend);
}
}
//归并有序序列并计算逆序数的个数
void MergeAndCountNum(int a[], int tmp[], int L, int R, int rightend)
{
int leftend = R - 1;
int i = L, j = R;
int k = L;
while (i <= leftend && j <= rightend)
{
if (a[i] < a[j])
tmp[k++] = a[i++];
else
{
tmp[k++] = a[j++];
sum = sum + (leftend-i+1);
}
}
while (i <= leftend)
tmp[k++] = a[i++];
while (j <= rightend)
tmp[k++] = a[j++];
for (i = L; i <= rightend; i++)
a[i] = tmp[i];
}
题目描述
考虑1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在 j,k,满足j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。一个排列含有逆序的个数称为这个排列的逆序数。例如排列2,6,3,4,5,1 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。
现给定1,2,…,n的一个排列,求它的逆序数。
问题分析
笨办法:O(n2)
分治O(nlogn):
1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(O(n)实现)
关键是左半边和右半边都是排好序的。比如,都是从小到大排序的。这样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的逆序个数
流程分析1:得到一串大序列abcdefg,然后调用MergeSort(得到总的逆序数),
对于左半边MergeSort(a, tmp, L, center)+右边c+跨边的逆序数MergeAndCountNum;
不过这个跨边的逆序数是要有前提的就是左边,右边都是有序的,所以才要MergeAndCountNum,不单单只是计算,还要排序,因为调用这个MergeSort(a, tmp, L, center)就把左边的顺便也都给排序好了,同理,右边也被MergeSort(a, tmp, L, center)里面的第三个函数给排序了