洛谷Oj-逆序对-分治(归并排序)

来源:互联网 发布:sql server 权限管理 编辑:程序博客网 时间:2024/06/04 20:03

问题描述
猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i

#define inf 2000000000//无穷大(并不是太专业)#define max 45000//数据规模为40000int L[max/2+2],R[max/2+2];//左子数组,右子数组long merge(int a[],int n,int left,int mid,int right)//合并(merge)函数,数组a,共n个元素,局部数组的开头元素,,局部数组末尾+1的元素{    int i,j,k;    long cnt=0;//计数变量    int n1=mid-left;//左闭右开区间[left,mid)的长度    int n2=right-mid;//左开右闭区间[mid,right)的长度    for(i=0;i<n1;i++)//执行n1次for循环        L[i]=a[left+i];//将a[left]到a[left+n1-1](代入n1即得a[mid-1])赋给L[0]到L[n1-1]    for(i=0;i<n2;i++)//执行n2次for循环        R[i]=a[mid+i];//将a[mid]到a[mid+n2-1](代入n2即得a[right-1])赋给R[0]到R[n2-1]    L[n1]=R[n2]=inf;将L[n1]与R[n2]赋为无穷大    i=0;//重置i    j=0;//重置j    for(k=left;k<=right-1;k++)//合并子数组L,R。并入数组a中,升序        if(L[i]<=R[j])//如果L[i]小于等于R[j],就将L[i]赋给a[k](k从left到right-1),并将i自增            a[k]=L[i++];        else//如果R[j]小于L[i],就将R[j]赋给a[k](k从left到right-1),并将j自增,这种情况下需要计算逆序对的个数(不存在逆序数的情况是数组L中每一个元素都比数组R中的元素小),我们需要知道在数组L中有几个元素比R[j]大        {            a[k]=R[j++];            cnt+=n1-i;i是此时数组a中来自数组L元素的个数,显然比R[j]大的元素的个数就是L[i+1]到L[mid-1]元素的个数,即数组L的大小n1减去i        }    return cnt;//返回计算变量cnt的值}long mergesort(int a[],int n,int left,int right)//归并排序函数,数组a,共n个元素,局部数组的开头元素,局部数组末尾+1的元素{    long v1,v2,v3;//分别计算三种情况的数目    if(left+1<right)//如果子数组不为空    {        int mid=(left+right)/2;//数组的中间位置mid        v1=mergesort(a,n,left,mid);//对应情况(1),宏观        v2=mergesort(a,n,mid,right);//对应情况(2),宏观        v3=merge(a,n,left,mid,right);//对应情况(3),宏观        return v1+v2+v3;//三种情况的数目相加并返回    }    else        return 0;}int main(){    int a[40001],n,i;//n表示数列中有n个数。    scanf("%d",&n);//输入n    for(i=1;i<=n;i++)//1下标数组,范围为1到n        scanf("%d",&a[i]);    printf("%ld\n",mergesort(a,n,1,n+1));    return 0;}

算法描述
①除了归并排序,冒泡排序也能求解逆序对的个数,只不过会TLE。
②假设我们要统计数列A中逆序对的个数。我们可以将数列A分成两半得到数列B和数列C。于是数列A中所有的逆序对必居下面三者其一:
(1):i,j都属于数列B的逆序对(i,j)。
(2):i,j都属于数列C的逆序对(i,j)。
(3):i属于数列B而j属于数列C的逆序对(i,j)。
③递归的过程就是不断缩小问题规模的过程,递归使得数组不断二分,直到所得子数组中元素个数为1(当子数组中元素个数为0时,函数返回0,所以说最终的状态是子数组元素个数为1的状态),我们不妨从这儿入手,既然左边和右边都已经被完全分割完了,那么就应该执行合并函数了。
合并过程请大家对照来自《挑战程序设计竞赛(第二版)》的归并排序的示意图领悟,在脑海中模拟调用和返回的过程

原创粉丝点击