求逆序數(逆序數 歸併排序)
一、題目描述
總時間限制:
1000ms
記憶體限制:
65536kB
描述
在Internet上的搜尋引擎經常需要對資訊進行比較,比如可以通過某個人對一些事物的排名來估計他(或她)對各種不同資訊的興趣,從而實現個性化的服務。
對於不同的排名結果可以用逆序來評價它們之間的差異。考慮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儲存。
開始沒有思路,我做的題目是沒有提示的,這個題目是上網貼上的,有提示.我開始是沒有想到歸併排序
有這樣一個題 1 4 3 2這四個數,每次交換相鄰的兩個需要交換幾次才能從小到大排序?先2--3交換,再2--4交換
這是因為4比2大且在2的前面,這就相當於是逆序對。
這道題 需要在歸併排序的時候統計逆序對的個數。
來自 <https://www.cnblogs.com/zzyh/p/6625872.html>
分析:本題難點在於分析如何用歸併法求其逆序數,先來看下逆序數的定義:
某一個數的逆序數等於在它之前有多少個比它大的數
某一個序列的逆序數等於所有數的逆序數之和
序列:9 1 0 5 4
逆序數: 4 + 1 + 1 = 6
瞭解了逆序數的定義後我們來用歸併法進行求解,歸併法的具體操作我就不贅述了,就套一個模板即可,寫完歸併的模板後只需在歸併處理兩個有序序列合併,且是前指標下標所指元素大於後指標下標所指元素時進行進行一次運算即可,什麼運算呢?我們想一下,當前半部分的有序序列的某個元素大於後半部分有序序列的某個元素時,那麼自然從前半部分指向的那個元素起到中間下標都是大於後面有序序列的,即比後面元素的逆序數為sum = mid - i + 1(i為前序列的指標下標,mid是中間下標)
如果你聽了還是不明白,估計是你歸併排序掌握的不牢,可以看看我另一篇關於歸併排序的介紹(嘛,雖然說的很水……)
https://blog.csdn.net/Oneplus6/article/details/85888026
#include <iostream>
using namespace std;
const int MAXN = 100005;
long sum = 0;
int temp[MAXN];
void Merge(long num[], long l, long mid, long r)
{
long i = l, j = mid + 1, k = l;
while(i<=mid && j<=r)
{
if(num[i] > num[j])
{
temp[k] = num[j];
k++;j++;
sum += mid - i + 1; //核心程式碼
}
else
{
temp[k] = num[i];
k++;i++;
}
}
while(i <= mid)
{
temp[k] = num[i];
k++;i++;
}
while(j <= r)
{
temp[k] = num[j];
k++;j++;
}
for(i=l; i<=r; i++)
{
num[i] = temp[i];
}
}
void MergeSort(long num[], long l, long r)
{
if(l >= r)
{
return;
}
long mid = (l+r)/2;
MergeSort(num, l, mid);
MergeSort(num, mid+1, r);
Merge(num, l, mid, r);
}
int main()
{
long n;
long num[MAXN];
cin >> n;
for(int i=0; i<n; i++)
{
cin >> num[i];
}
MergeSort(num, 0, n-1);
cout << sum << endl;
return 0;
}