标签:
给你一个数组,求该数组的逆序数
输入文件第一行包含一个自然数N,N个数 接下来有N行,表示a[0],a[1]...a[n - 1]
输出文件仅有一行包含一个整数,表示该数组的逆序数
3
575085724
344369358
808884464
1
==============================================
所谓逆序数,就是一个数列中的元素,比它大并且排在它前面的数的个数,然后对每个元素逆序数求和就是答案。
如果直接做的话,对每个数都要扫描它前面的所有数,复杂度为n^2,因此可以用归并排序的思想。所谓归并排序,有点类似分治法,比如4,1,3,2,首先把序列分为两半并调用归并函数(递归直到只有一个元素)分别得到两个有序子序列,然后对这两个子序列进行归并.归并过程是这样的:
对两个子序列同时从左到右扫描,选取较小值加入序列。对于1,4和2,3,选取顺序是为:1->2->3->4.
那么怎么统计逆序数对呢?举个例子,当扫描到3时,对于它自身所在子序列:在它以前的元素肯定比它小,不计,在它后面的元素在因为在对这个子序列排序时已经计算过了(原来是3,2,子序列分别为3和2,那么扫描到2的时候会对3统计)也不用计。对于这个时刻和它进行比较的子序列:此时和它比较的元素以前的肯定比它小(如1,4中的元素4以前的元素(1)),不计,只要计算和它比较的元素本身及之后的元素就行了。
因此,等到排完序(nlogn)后,逆序数对也就求出来了。
具体代码:
1 #include <iostream> 2 using namespace std; 3 long long n,count = 0; 4 long long arr[100000],a[100000],b[100000];//要计算的序列和两个子序列 5 void mergesort(int , int); 6 void merge(int, int, int);//归并过程 7 int main(){ 8 int n; 9 10 cin>>n; 11 for (int i = 0; i < n;++i) cin>>arr[i]; 12 mergesort(0,n-1); 13 cout<<count; 14 15 return 0; 16 } 17 void merge(int l,int m,int r){ 18 if (l==r) return; 19 int len1 = m - l + 1,p = 0,q = 0; 20 int len2 = r - m; 21 for (int i = 0;i < len1;++i) a[i] = arr[i+l]; 22 for (int i = 0;i < len2;++i) b[i] = arr[m+i+1]; 23 a[len1] = 1000000000000000000; 24 b[len2] = 1000000000000000000; 25 for (int i = l;i <= r;++i){ 26 if (a[p] <= b[q]) arr[i] = a[p++]; 27 else{ 28 count += m - p -l + 1;//计算逆序数对 29 arr[i] = b[q++]; 30 } 31 } 32 } 33 void mergesort(int l,int r){ 34 if (l>=r) return; 35 int m = (l+r)/2; 36 mergesort(l,m); 37 mergesort(m+1,r); 38 merge(l,m,r); 39 }
标签:
原文地址:http://www.cnblogs.com/wenma/p/4550624.html