时间限制: 300 ms 内存限制: 65536 kb
题面
题目链接
给定一个整数序列,求逆序对的个数
输入
第一个数为序列长度n
接下来一行,n个整数,保证在int范围内
输出
输出一行,逆序对的个数
输入样例
5
5 4 3 2 1
输出样例
10
数据范围
1≤n≤105
解题思路
思路
一般思路是对于每一个数据都向后搜索比它大的数,但这种方法的时间复杂度是o(n2)
这样的比较方式有没有让你联想到选择排序?选确定要排的元素的位置,再往后逐个寻找该选择哪个元素和选定元素的位置互换。只不过在求逆序对的时候我们并没有将原数组排序而已。
由此我们可以想到,可以将“寻找逆序对”的过程类比成排序里的“比较”过程,因此寻找更优的排序方式可以降低比较的次数并时间复杂度。
这里我们可以运用分治思想,其中的典型就是归并排序。
(图片来源:https://zhuanlan.zhihu.com/p/124356219)
因为子数组已经是有序的了,我们只需要在两个子数组合并成一个大数组的时候,看看位于后面的数组中的元素在被合并时,前一个数组还有多少个元素没有插入,就知道前一个数组中有多少数比它大了。(递归到最底层只有2个数的情况也是如此)
最后我们只需要把每一次求出来的个数相加就能求得逆序对的个数。
AC代码
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include <malloc.h>
long long int count=0;//int不够存
void merge(int x[ ],int tmp[ ],int left,int leftend,int rightend)
{
int i=left, j=leftend+1, q=left;
while(i<=leftend && j<=rightend)
if(x[i]<=x[j])
{tmp[q++]=x[i++];}
else
{
tmp[q++]=x[j++];
count+=(leftend-i+1);//当插入后面的数组的元素的时候,看看前面的数组还有多少元素没有插入
}
while(i<=leftend)
tmp[q++]=x[i++];
while(j<=rightend)
tmp[q++]=x[j++];
for(i=left; i<=rightend; i++) x[i]=tmp[i];
}
void mSort(int k[], int tmp[], int left, int right)
{
int center;
if(left < right){
center = (left+right)/2;
mSort(k, tmp, left, center);
mSort(k, tmp, center+1, right);
merge(k, tmp, left,center, right);
}
}
void mergeSort(int k[ ],int n)
{
int *tmp;
tmp = (int *)malloc(sizeof(int) * n);
if(tmp != NULL) {
mSort(k, tmp, 0, n-1);
free(tmp);
}
else
printf("No space for tmp array!!!\n");
}//以上三段是经典的归并排序,求逆序对的时候仅仅加了一行代码而已
int main()
{
int n,i,j;
scanf("%d",&n);
int *a=(int*)malloc(n*sizeof(int));
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
mergeSort(a,n);
printf("%lld",count);
free(a);
return 0;
}