1. 程式人生 > >[數算MOOC]求逆序對(歸併排序)

[數算MOOC]求逆序對(歸併排序)

首先介紹歸併排序,它是指對一個數組,劃分為兩個。對兩個陣列分別排序,兩個陣列排序好後合併。

合併的過程為:從兩個陣列取第一個數,下標i,j,比較,數值比較小的複製到一個輔助陣列中,然後下標++即可。如果有一個數組提前結束,把另外一個數組複製到輔助陣列中。然後把輔助陣列複製給原陣列即完成排序。通過遞迴,很容易實現。

利用歸併排序求逆序對該怎麼求呢?

已經排序好的陣列逆序對為0(廢話)

兩個陣列歸併,如果左陣列中的元素a[i]大於右陣列a[j],歸併陣列則的逆序對+mid-i+1即可(a[i]右面的左陣列元素都大於a[j])

這樣累加即可

題目描述

總時間限制: 
500ms 
記憶體限制: 
65536kB
描述

對於一個長度為N的整數序列A,滿足i < j 且 Ai > Aj.的數對(i,j)稱為整數序列A的一個逆序
<j<=n且ai><j<=n且ai><j<=n且ai><j<=n且ai>

請求出整數序列A的所有逆序對個數

輸入
輸入包含多組測試資料,每組測試資料有兩行
第一行為整數N(1 <= N <= 20000),當輸入0時結束
第二行為N個整數,表示長為N的整數序列
輸出
每組資料對應一行,輸出逆序對的個數
樣例輸入
5
1 2 3 4 5
5
5 4 3 2 1
1
1
0
樣例輸出
0
10
0

上程式碼

#include<iostream>
using namespace std;
const int maxn=20005;
int n;
int num[maxn];
int temp[maxn];
int count;
void Scanf() {
    cin >> n;
}
void Input() {
    for(int i=0; i<n; i++)
        cin >> num[i];
}
void Merge(int left, int mid, int right) {
    int index1=left, index2=mid+1;
    int i=left;
    for(int j=left; j<=right; j++)
        temp[j]=num[j];
    while(index1<=mid && index2<=right) {
        if(temp[index1]<=temp[index2])
            num[i++]=temp[index1++];
        else {
            num[i++]=temp[index2++];
            count+=mid-index1+1;
        }
    }
    while(index1<=mid)
        num[i++]=temp[index1++];
    while(index2<=right)
        num[i++]=temp[index2++];
}
void Merge_sort(int left, int right) {
    if(left<right) {
        int mid=(left+right)>>1;
        Merge_sort(left, mid);
        Merge_sort(mid+1, right);
        Merge(left, mid, right);
    }
}
void Init() {
    count=0;
}
void Print() {
    cout << count << endl;
}
int main() {
    Scanf();
    while(n!=0) {
        Input();
        Init();
        Merge_sort(0, n-1);
        Print();
        Scanf();
    }
}