/*
逆序对数:
给出一列数a1,a2,...,an,求它的逆序对数,即有多少个有序对(i,j),使得i<j但ai>aj。n可以高达10^6
思路:
分解成前后两个序列,统计后序列中每个元素与前面中每个元素的逆序对数,是一个叉乘
书析:
采用O(n^2)枚举超时,因为n很大。
划分:将序列等分
递归求解:统计i和j均在左边或者均在右边的逆序对个数
合并:统计i在左边,但j在右边的个数
划分与递归都好办,
合并:如何求出i在左边,j在右边的逆序对数目。
技巧:分类,按照j的不同把这些“跨越两边”的逆序对进行分类,对于右边的每个j,统计左边比它大的元素个数f(j),所有f(j)之和便是答案。
合并操作是从小到大进行的,当右边的A[j]复制到T中时,左边没来得及复制到T的那些数就是左边所有比A[j]大的个数,及m-p,左边所剩元素在[p,m)中
因此只需要在
else
{
iTempArr[i++] = iArr[m];
iCnt += mid - l;//只需要加上这一句即可
}
输入:
8
1 9 6 3 4 7 9 0
输出:
0 1 3 4 6 7 9 9
13
(1+5+3+1+1+1+1=13)
*/
/*
关键:
1 采用O(n^2)枚举超时,因为n很大。 递归求解:统计i和j均在左边或者均在右边的逆序对个数 合并:统计i在左边,但j在右边的个数
2 技巧:分类,按照j的不同把这些“跨越两边”的逆序对进行分类,对于右边的每个j,统计左边比它大的元素个数f(j),所有f(j)之和便是答案。
3 合并操作是从小到大进行的,当右边的A[j]复制到T中时,左边没来得及复制到T的那些数就是左边所有比A[j]大的个数,及m-p,左边所剩元素在[p,m)中
因此只需要在
else
{
iTempArr[i++] = iArr[m];
iCnt += mid - l;//只需要加上这一句即可
}
4 if(m >= high || (l < mid && iArr[l] <= iArr[m]))//=与号不能漏
5 快速排序:
划分:数组重排后分成两部分,左边均<=右边
递归求解:把左右两部分分别排序
合并:不用合并,数组已经完全有序
*/
#include <stdio.h>
#define MAXSIZE 1024
int iCnt;
void mergeSort(int* iArr,int low,int high,int* iTempArr)
{
if(1 < high - low)
{
int mid = low + (high - low) / 2 ;
int l = low,m = mid,i = low;
mergeSort(iArr,low,mid,iTempArr);
mergeSort(iArr,mid,high,iTempArr);
while(l < mid || m < high)
{
//if(m >= high || (l < mid && iArr[l] < iArr[m]))
if(m >= high || (l < mid && iArr[l] <= iArr[m]))//=与号不能漏
{
iTempArr[i++] = iArr[l++];
}
else
{
iTempArr[i++] = iArr[m++];
iCnt += mid - l;
}
}
for(int j = low ; j < high ; j++)
{
iArr[j] = iTempArr[j];
}
}
}
void print(int* iArr,int n)
{
for(int i = 0 ; i < n; i++)
{
if(i)
{
printf(" %d",iArr[i]);
}
else
{
printf("%d",iArr[i]);
}
}
printf("\n");
}
void process()
{
int n;
while(EOF != scanf("%d",&n))
{
iCnt = 0;
int iArr[MAXSIZE];
int iTempArr[MAXSIZE];
for(int i = 0; i < n ; i++)
{
scanf("%d",&iArr[i]);
}
mergeSort(iArr,0,n,iTempArr);
print(iArr,n);
printf("%d\n",iCnt);
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}
算法竞赛入门经典:第八章 高效算法设计 8.3归并排序应用之逆序对数
最新推荐文章于 2020-02-28 03:02:52 发布