问题: 输入一个长度长度为n的数组A[n],求出数组A[n]逆序对的总数。 输入: 长度为n的数组A[n] 输出: 数组A[n]逆序对的总数
与最大子数组的问题一样,执行效率的瓶颈值也在合并方面,也就是S3跨越子数组的逆序对数目的求解问题。
1. 策略一:直接求解 (1)对每个Aj]∈ A[m +1…n],枚举A[i]∈ A[1…m]并统计逆序对数目 (2)求解S的算法运行时间:O(n2) 直接求解的运行时间不是很理想,所以,有没有更好的方法呢?
2. 策略二:排序求解 (1)分别对数组A[1…m]和A[m+ 1…n]进行排序; (2)对于每个A[ j ]∈A[m+1…n],采用二分查找为其在A[1…m]中定位; (3)A[ j ]在A[1…m]定位点右侧的元素均可与A[ j ]构成逆序对; (4)求解S的算法运行时间:O(nlogn)。
排序求解S的分而治之提高了算法运行时间,但仍然还有优化的可能;
3. 策略三:归并求解 (1)从左到右扫描有序子数组:A[i]∈A[1…m],A[j] ∈ A[m+1…n] ···如果A[i] > A[j],统计逆序对,j向右移 ···如果A[i] ≤ A[j],i向右移 (2)利用归并排序框架保证合并后数组的有序性 (3)S时间复杂度降至O(n)
题目描述 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。
比如 1 3 2 的逆序数就是1。
输入 第一行输入一个整数T表示测试数据的组数(1<=T<=5) 每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000) 随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。
数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。 输出 输出该数列的逆序数 样例输入 2 2 1 1 3 1 3 2 样例输出 0 1
代码:
#include <iostream> #include <cstdio> using namespace std; #define max 1000001 long long a[max],b[max]; long long count; void Merge(long long a[], int l, int mid , int r) //归并排序的合并部分 { int i = l, j = mid + 1,k = l; //遍历统计逆序数对 while(i <= mid&&j <= r){ if(a[i] <= a[j]) b[k++] = a[i++]; else{ count += j - k; b[k++] = a[j++]; } } while(i <= mid) //左半部分排序 b[k++] = a[i++]; while(j <= r) //右半部分排序 b[k++] = a[j++]; for(int i = l; i <= r; i++) //从辅助空间复制回 a 数组 a[i] = b[i]; } void MergeSort(long long a[], int l, int r) //归并排序 { if(l < r){ int mid = l + (r-l)/2; MergeSort(a,l,mid); // 左半部分递归求解 MergeSort(a,mid+1,r); // 右半部分递归求解 Merge(a,l,mid,r); // 合并前后两个部分 } } int main(){ int n; for(int i = 0; i < n; i++) scanf("%d",&a[i]); MergeSort(a,0,n-1); printf("%lld\n",count); return 0; } int main(){ int n,m; scanf("%d",&n); while(n--){ scanf("%d",&m); count = 0; for(int i = 0; i < m; i++) scanf("%d",&a[i]); MergeSort(a,0,m-1); printf("%lld\n",count); } return 0; }