求排列的逆序數(分治)


考慮1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在j,k,滿足 j < k 且 ij > ik, 那么就稱(ij,ik)是這個排列的一個逆序。
一個排列含有逆序的個數稱為這個排列的逆序數。例如排列 263451 含有8個 逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此該排列的逆序數就是8。
現給定1,2,…,n的一個排列,求它的逆序數。

笨辦法:O(n2)

分治O(nlogn):
1) 將數組分成兩半,分別求出左半邊的逆序數和右半邊的逆序數
2) 再算有多少逆序是由左半邊取一個數和右半邊取一個數構成(要求O(n)實 現)

由歸並排序改進得到,加上計算逆序的步驟

MergeSortAndCount: 歸並排序並計算逆序數

代碼:

#include<iostream>
using namespace std;
#define N 100000 + 5
int n; 
int a[N];
int b[N];
int ans = 0;
void merge(int L, int R) {
    int mid = (L + R) / 2;
    int l = L;
    int r = mid+1;
    int count = 0;
    while(l <= mid && r <= R) {
        if(a[l] < a[r]) {
            b[count++] = a[r++];    
        } else {
            b[count++] = a[l++];
            ans += R - r + 1;
        }
    }
    while(l <= mid) b[count++] = a[l++];
    while(r <= R) b[count++] = a[r++];
    for(int i = L; i <= R; i++) a[i] = b[i-L];
}
void MergeSortAndCount(int L, int R) {
    if(L < R) {
        int mid = (L + R) / 2;
        MergeSortAndCount(L, mid);
        MergeSortAndCount(mid + 1, R);
        merge(L, R);
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    MergeSortAndCount(0, n-1);
    printf("%d\n", ans);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM