首页 > 代码库 > poj1804(归并排序求逆序数)

poj1804(归并排序求逆序数)

逆序数,也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。

我们移动元素的次数转化为,假如对每个数da[i]来说前面比他大的数的数目为c[i]的话,那么移动元素总次数就应该是c[0]+c[1]+……+c[n-1],就是数列的逆序数。

一般解决有两种思路。

1、归并排序,归并排的话一定是最好的方案,主观上想一下每一个数都在向目标位置前进(学过数据结构的话有个名词叫稳定排序,另一个稳定排序是冒泡,选择明显不是)。

2、用线段树树状数组来做,求逆序在另一篇博文中有提。

复杂度来说,先说两种排序,冒泡为O(n^2),归并O(nlog(n)),树状数组也是O(nlog(n)),

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rev(i,a,b) for(int i=(a);i>=(b);i--)
#define clr(a,x) memset(a,x,sizeof a)
#define inf 0x3f3f3f3f
typedef long long LL;
using namespace std;

const int mod=1e9 +7;
const int maxn=2005;
const int maxm=4005;
int a[maxn],tmp[maxn];
int ans;

void Merge(int l,int m,int r)
{
    int i=l;
    int j=m+1;
    int k=l;
    while(i<=m&&j<=r)
    {
        if(a[i]>a[j])
        {
            tmp[k++]=a[j++];
            ans+=m-i+1;
        }
        else
        {
            tmp[k++]=a[i++];
        }
    }
    while(i<=m)tmp[k++]=a[i++];
    while(j<=r)tmp[k++]=a[j++];
    for(int i=l;i<=r;i++)
        a[i]=tmp[i];
}

void Merge_sort(int l,int r)
{
    if(l<r)
    {
        int m=(l+r)>>1;
        Merge_sort(l,m);
        Merge_sort(m+1,r);
        Merge(l,m,r);
    }
}

int main()
{
    int t,n,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        ans=0;
        Merge_sort(0,n-1);
        printf("Scenario #%d:\n%d\n\n",cas++,ans);
    }
    return 0;
}


poj1804(归并排序求逆序数)