【题目描述:】
猫猫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集合内的数字都是已经从小到大排好了的,
所以如果Ai > 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 ", ans); 31 }