题目内容:
设a1, a2,…, an是集合{1, 2, …, n}的一个排列,如果i<j且ai>aj,则序偶(ai, aj)称为该排列的一个逆序。例如,2, 3, 1有两个逆序:(3, 1)和(2, 1)。设计算法统计给定排列中含有逆序的个数。
输入格式:
第一行输入集合中元素个数n,第二行输入n个集合元素
输出格式:
含有逆序的个数
输入样例:
3
2 3 1
输出样例:
2
解题思路:
如果采用蛮力法求解,即设置一个count=0变量,将两次for循环嵌套调用,每次发现是逆序数则count++,这样的时间复杂度为O(n^2),当n值过大时,效率不高。
使用分治法求解可以使得时间复杂度降为O(nlogn)。此题使用分治法的核心在于三步:第一步,将集合二分后求前半部分的逆序数。第二步,求集合二分后的后半部分的逆序数。第三分,两个集合之间的逆序数。在第三步即可使用归并法来完成,而核心在于每次求两个集合的逆序数时,这两个集合应该为有序集合,这样将其归并时,可直接求得逆序数。因此在每次调用count_together这个函数时参与在其中的集合应该是有序集合。
代码示例:
#include<iostream>
using namespace std;
//n为左边集合的起点,k为右边集合的起点,m为终点,总共m-n+1个元素
int count_together(int n, int k, int m,int* arr) {
int i = n, j = k;
int num = 0;
int* b;
b = (int*)malloc((m - n + 1) * sizeof(int));
int x = 0;
while(i<k&&j<=m)
{
if (arr[i] > arr[j])
{
b[x] = arr[j];
x++;
num = num + k - i;
j++;
}
else
{
b[x] = arr[i];
x++;
i++;
}
}
while(i < k)
{
b[x] = arr[i];
i++;
x++;
}
while(j <= m)
{
b[x] = arr[j];
j++;
x++;
}
for (int i = n; i <= m; i++)
arr[i] = b[i-n];
return num;
}
//n为起点,k为终点
int count_num(int n, int k, int* arr) {
if (n >= k)
return 0;
int num_1, num_2, num_3;
int mid = (n + k) / 2;
num_1 = count_num(n, mid, arr);
num_2 = count_num(mid + 1, k, arr);
num_3 = count_together(n, mid + 1, k, arr);
return num_1 + num_2 + num_3;
}
int main()
{
int n;
cin >> n;
int* arr;
arr = (int*)malloc(n * sizeof(int));
for (int i = 0; i < n; i++)
cin >> arr[i];
int num = count_num(0,n-1, arr);
cout << num;
}