求逆序对 (用归并) nlogn

来源:互联网 发布:mac免费打谱软件 编辑:程序博客网 时间:2024/05/21 10:42

逆序对 在线测评 洛谷:
https://www.luogu.org/problem/show?pid=1908
什么是逆序对??
对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。(来自百度百科)

怎么求 有一个广为人知的 n方 的算法 就是枚举
在此讲一下 用归并排序 复杂度是nlogn的算法

贴代码:

#include<iostream>#include<cstdio>using namespace std;const int INF=40010;int a[INF];int t[INF];int cnt;void mid_sort(int x,int y){    if(y-x>1)//我们排序的是[x,y)的 所以当y-x为一时就只剩下一个数了 此时 我们就没有必要給一个数排序了     {        int m;//  m时x和y中点         m=x+(y-x)/2;         int l=x;        int r=m;        int i=x;        mid_sort(x,m);/*这是递归来排序*/         mid_sort(m,y);//同上         while(l<m||r<y) //从这里往下是归并排序中的 合并两串已经排好序的数         {            if(r>=y||(l<m && a[l]<=a[r]))             t[i++]=a[l++];            else{            t[i++]=a[r++];            cnt+=m-l;                       //在下面会贴出来             }        }        for(int i=x;i<y;i++)        {            a[i]=t[i];        }                                    //从这里往上      }}int main (){    int n;    cin>>n;    for(int i=1;i<=n;i++)    {        cin>>a[i];    }    mid_sort(1,n+1);/*因为归并排序 是排序[1,n)的数,所以我们要把n+1*/     cout<<cnt;    return 0;}
        while(l<m||r<y)                              {            if(r>=y||(l<m && a[l]<=a[r]))             t[i++]=a[l++];            else{            t[i++]=a[r++];            cnt+=m-l;                    }        }        for(int i=x;i<y;i++)        {            a[i]=t[i];        }   

首先讲一下归并排序 会的直接跳过就好
先看其中的判断

if(r>=y||(l<m && a[l]<=a[r]))

如果我们只是单纯的去比较当前的左区间和右区间 当前的数的大小从而决定将哪个数放入临时数组 这样是有一个bug 的 就是如果有一个区间空了怎么办
所以 我们在此分为三种情况
其中第一种情况是:如果右区间为空 此时还在while循环里 也就是说 左区间一定非空 所以此时我们只需要将左区间剩下的数复制进临时数组即可。
另一种情况是:如果右区间不是空的 此时如果左区间也是非空的(这就是l < m的意义) 且 a[l]<=a[r] 我们就复制左区间的数。
剩下的情况就是复制右区间的条件了。

     for(int i=x;i<y;i++)    {        a[i]=t[i];    }

这是将临时数组中的数复制到原数组的过程
end
接下来是逆序对的求法
很简单只需要加一条语句就行
在刚刚的代码已经贴出来了:

        cnt+=m-l;  

cnt 是逆序对的总数
为什么这样做:
当我们从右区间拿出来一个数a之后 此时就代表着左区间剩下的数一定比当前从右区间拿出的数大 所以此时左区间剩下的数就是有关a的逆序对数
end

原创粉丝点击