《程序設計與算法(二)算法基礎》《第五週 分治》求排列的逆序數 11

011:求排列的逆序數

總時間限制: 
1000ms
 
內存限制: 
65536kB
描述

在Internet上的搜索引擎常常須要對信息進行比較,好比能夠經過某我的對一些事物的排名來估計他(或她)對各類不一樣信息的興趣,從而實現個性化的服務。ios

對於不一樣的排名結果能夠用逆序來評價它們之間的差別。考慮1,2,…,n的排列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 構成的全部n!個排列中,最小的逆序數是0,對應的排列就是1,2,…,n;最大的逆序數是n(n-1)/2,對應的排列就是n,(n-1),…,2,1。逆序數越大的排列與原始排列的差別度就越大。數組

現給定1,2,…,n的一個排列,求它的逆序數。


函數

輸入
第一行是一個整數n,表示該排列有n個數(n <= 100000)。
第二行是n個不一樣的正整數,之間以空格隔開,表示該排列。
輸出
輸出該排列的逆序數。
樣例輸入
6
2 6 3 4 5 1
樣例輸出
8
提示
1. 利用二分歸併排序算法(分治);
2. 注意結果可能超過int的範圍,須要用long long存儲。
/*
http://cxsjsxmooc.openjudge.cn/2019t2summerall/011/
求排列的逆序數
MergeSort
歸併排序
複雜度: n*log(n)
*/
#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
using namespace std;
long long MergeSort(int a[], int s, int e, int tmp[]);
void Merge(int a[], int s, int m, int e, int tmp[]);
//int a[] = { 2,6,3,4,5,1 };
//int b[6];
int a[100010];
int b[100010];

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
        scanf("%d", &a[i]);
    printf("%lld", MergeSort(a, 0, n - 1, b));
    return 0;

    //int size = sizeof(a) / sizeof(int);
    //long long result = MergeSort(a, 0, size - 1, b);
    //for (int k = 0; k < size; k++)
    //    cout << a[k] << " ";
    //cout << "\n" ;
    //cout << result << endl;
    //return 0;
}
/*歸併排序 s :數組開始 e :數組結尾*/
// 將數組 a 的局部 a[s, m] 和 a[m + 1, e] 合併到 tmp, 並保證 tmp 有序,而後再拷貝回 a[s, m]
// 歸併操做時間複雜度: O (e-m+1), 即 O (n)
long long count(int a[], int s, int m, int e)
//從大到小合併[s,m], [m+1,e] 
{
    long long result = 0;
    int p1 = s;
    int p2 = m + 1;
    while (p1 <= m && p2 <= e)
    {
        if (a[p1] > a[p2])
        {
            result += e - p2 + 1;
            p1++;
        }
        else
        {
            p2++;
        }
    }
    return result;

}

long long MergeSort(int a[], int s, int e, int tmp[])
{
    long long result = 0;
    if (s < e) /*當只有一個元素時,遞歸終止,直接調用merge函數,比較 13和27大小,最後排序*/
    {
        int m;// cut array into half
        m = s + (e - s) / 2;
        result += MergeSort(a, s, m, tmp);/* 排好序的數組 臨時放在哪裏? */
        result += MergeSort(a, m + 1, e, tmp);//分別求兩邊的逆序數 
        result += count(a, s, m, e);//而後再o(n)算左邊和右邊形成的逆序數 。此時要求左邊和右邊都是從大到小有序的,才能在o(n)時間內算出結果 
        Merge(a, s, m, e, tmp);//從大到小合併,確保排序 
    }
    return result;

}
void Merge(int a[], int s, int m, int e, int tmp[])
//從大到小合併[s,m], [m+1,e] 
{
    int i = 0;
    int p1 = s;
    int p2 = m + 1;
    while (p1 <= m && p2 <= e)
    {
        if (a[p1] > a[p2])
        {
            tmp[i++] = a[p1++];
        }
        else
        {
            tmp[i++] = a[p2++];
        }
    }
    while (p1 <= m)
        tmp[i++] = a[p1++];
    while (p2 <= e)
        tmp[i++] = a[p2++];
    for (int j = 0; j < e - s + 1; j++)
    {
        a[s + j] = tmp[j];
    }

}
相關文章
相關標籤/搜索