poj 2299 Ultra-QuickSort【归并排序求逆序数】
题目大意:
给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。
这个题数据量大,虽然给7s的时间,但是冒泡的话肯定超时。
这个题让你求的是逆序数,我们用归并排序来求。
一个乱序序列的 逆序数 = 在只允许相邻两个元素交换的条件下,得到有序序列的交换次
在做这道题前,我参考了《大话数据结构》 编著:程杰 P406, 《c/c++函数与算法速查手册》 编著:陈锐 P582.
View Code
#include<stdio.h> #include<limits.h> const int MAXN = 500000 + 10; const int INF = INT_MAX; long long tot;//tot为逆序数总数。 int arr[MAXN]; //将b数组中的元素复制到a数组中。 void CopyArray(int a[], int b[], int len, int left) { for(int i = 0; i < len; i++) { a[left++] = b[i]; } } //归并排序,合并两个子序列中的元素。 void Merge(int a[], int left, int right) { int begin1, begin2, mid, k=0, len; begin1 = left; mid = (left+right)/2; begin2 = mid+1; len = right-left+1; int *b = new int[len]; while(begin1 <= mid && begin2 <= right) { if(a[begin1] < a[begin2]) { b[k++] = a[begin1++]; } else { b[k++] = a[begin2++]; tot += (mid - begin1 + 1); //当后面的有序序列中的元素小与前面的有序序列的元素, //那么总的逆序数要加上前面有序序列中剩余的元素的个数, //因为这些是有序的,所以加上的那些数每个都比后面的序列中参与比较的元素大。 } } while(begin1 <= mid) { b[k++] = a[begin1++]; } while(begin2 <= right) { b[k++] = a[begin2++]; } CopyArray(a, b, len, left); delete b; } //归并排序。 void Msort(int a[], int s, int t) { int m; if( s<t ) { m = ( s+t ) /2; Msort(a, s, m); Msort(a, m+1, t); Merge(a, s, t); } } int main() { int n; while(scanf("%d", &n), n) { tot = 0; for(int i = 0; i < n; i++) { scanf("%d", &arr[i]); } Msort(arr, 0, n-1); printf("%lld\n", tot); } return 0; }