1. 程式人生 > >逆序對

逆序對

下一個 href ace clas lowbit class 排序 二分 排序。

什麽是逆序對??

我們在這裏給出一個定義:如果i<j&&a[i]>a[j]的一對數稱為逆序對。

為什麽??!!

背過!!(都說了是定義了!)(其實我也不知道為什麽 orz (*^__^*) ……)

但是知道逆序對有什麽用處呢?

  • 具體用處我也不是很清楚,但是目前我們可以用逆序對的個數求將一列數排成有序的一列數交換的次數

怎樣求逆序對呢??

我們在這裏給出兩種算法:歸並排序求逆序對;樹狀數組求逆序對。

          §歸並排序求逆序對

歸並排序的核心思想是二分。

我們將一個序列a1,a2,a3,a4,a5,a6.........an;進行歸並排序

對於歸並排序還有童鞋不明白嗎?

我們在這裏就簡單提一下歸並排序吧。

說起排序來了,有人肯定又要說,要歸並排序幹嘛?sort不就挺好的嗎?又快有省時。所以有些人就不重視歸並排序。但是試問,你的sort可以求逆序對嗎?

顯然是不能的!所以我們還是要好好學歸並排序的(算了,扯多了)

下面我們來說說歸並排序吧!

所謂歸並排序就是將一組數進行無數次分割,將它劃分成一個個有序的區間,然後再將這一個個有序的區間一個個合並成有序的區間。

怎麽劃分?

那這就是一個二分的過程了。我們每次將一段數進行分開,我們在這裏引入一個mid為中間位置,一直將l到mid進行分,知道mid=l,這說明他已經將一個個連續的區間劃分成一個個獨立的區間了。這樣我們在在一個個進行合並的時候由於我們劃分成兩個區間(即兩個兩個區間進行合並),並且每一個區間都是有序的。

實際上歸並排序的交換次數就是這個數組的逆序對個數,為什麽呢?

我們可以這樣考慮:

歸並排序是將數列a[l,h]分成兩半a[l,mid]和a[mid+1,h]分別進行歸並排序,然後再將這兩半合並起來。

在合並的過程中(設l<=i<=mid,mid+1<=j<=h),當a[i]<=a[j]時,並不產生逆序數;當a[i]>a[j]時,在

前半部分中比a[i]大的數都比a[j]大,將a[j]放在a[i]前面的話,逆序數要加上mid+1-i。因此,可以在歸並

排序中的合並過程中計算逆序數.

這樣對於歸並排序求逆序對其實就是在歸並排序的基礎上加上了一個統計逆序對的過程。

那樣如果我們在進行合並的時候找到前一個區間的值比後一個區間的值大,那樣是不是就說明從這個數往後的所有數都比這個數小,那樣的話就說明從這個數往後的所有數多會與下一個區間的數形成逆序對,那樣的話我們在統計逆序對個數的時候就直接將這個區間的之後比這個數大的數的個數。以此類推。。。

下面附上代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100001
using namespace std;
int n,a[N];
long long ans;
void gsort(int l,int r)
{
     if(l==r) return ;
    int mid=(l+r)/2;int tmp[N];
    gsort(l,mid),gsort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) tmp[k++]=a[i++];
        else
        {
            ans+=mid-i+1;
            tmp[k++]=a[j++];
        }
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(int i=l;i<=r;i++) a[i]=tmp[i];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    gsort(1,n);
    cout<<ans;
    return 0;
} 

          §樹狀數組求逆序對

(這種做法我就沒打算用)

從大佬博客裏粘了個代碼,就湊活著看看吧。。。

#include <algorithm>
#include <cstdio>

#define lowbit(x) (x&(-x))

using namespace std;

const int N(40000+15);
int n,x,c[N],ans;
struct Node
{
    int num,mark;
}a[N];

bool cmp(Node a,Node b)
{
    return a.num>b.num;
}

inline void up(int x)
{
    for(;x<=N;x+=lowbit(x)) c[x]++;
}

inline int query(int x)
{
    int ret=0;
    for(;x;x-=lowbit(x)) ret+=c[x];
    return ret;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].num),a[i].mark=i;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {

        ans+=query(a[i].mark);
        up(a[i].mark);
    }
    printf("%d",ans);
    return 0;
}

大佬博客http://www.cnblogs.com/Shy-key/p/6930563.html

逆序對