【洛谷】【归并排序】P1908 逆序对

【题目描述:】

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

【输入格式:】

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

【输出格式:】

给定序列中逆序对的数目。

【算法分析:】

定义:

  对于序列a,如果i < j 且 a[i] > a[j],则称a[i]、a[j]为a序列的一组逆序对

由定义可知,一个长度为n的序列最多有n * (n - 1) / 2个逆序对,注意n的取值范围,一些情况下ans要开long long

冒泡求逆序对:

求逆序对可以用冒泡排序来做,每次交换相邻两个元素的时候逆序对的个数便增加一

冒泡排序(从小到大)其实就是通过消除逆序对来保证序列的有序性。

但是每次只找到一个逆序对效率太低,时间复杂度O(n2)

归并排序求逆序对:

还有一种稳定的排序算法:归并排序,也可以用来求逆序对的数量

假设[l, mid]区间内的元素构成集合A,[mid + 1, r]内的元素构成集合B

由于A、B集合内的数字都是已经从小到大排好了的,

所以如果A> Bj ,即这两个元素能够构成一个逆序对,

则 A[i, mid] 中的所有元素都可以和Bj构成逆序对

这样子就找到了一组一组地求逆序对的方法,时间复杂度O(nlog2n)

【代码:】

 1 //归并排序求逆序对
 2 #include<iostream>
 3 #include<cstdio>
 4 using namespace std;
 5 
 6 const int MAXN = 100000 + 1;
 7 
 8 int n, a[MAXN], tmp[MAXN];
 9 long long ans;
10 
11 void Merge_sort(int l, int r) {
12     if(l == r) return;
13     int mid = (l + r) >> 1;
14     Merge_sort(l, mid);
15     Merge_sort(mid + 1, r);
16     int i, j, cnt = l;
17     for(i = l, j = mid + 1; i <= mid; i++) {
18         for(; a[j] < a[i] && j <= r; j++)
19             tmp[cnt++] = a[j], ans += mid - i + 1;
20         tmp[cnt++] = a[i];
21     }
22     for(; j <= r; j++) tmp[cnt++] = a[j];
23     for(i = l; i <= r; i++) a[i] = tmp[i];
24 }
25 int main() {
26     scanf("%d", &n);
27     for(int i = 1; i <= n; i++)
28         scanf("%d", &a[i]);
29     Merge_sort(1, n);
30     printf("%lld\n", ans);
31 }

猜你喜欢

转载自www.cnblogs.com/devilk-sjj/p/9037849.html