计算逆序对/逆序数

利用归并排序计算逆序对

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;

long long ans;
int a[1000000];
void merge(int *a , int first, int mid, int last)
{
    int * temp = new int[last - first + 1];     
    int first1 = first, last1 = mid;
    int first2 = mid + 1, last2 = last;
    int index = 0;
    while(first1 <= last1 && first2 <= last2)
    {
        if(a[first1 ] <= a[first2])
        {
            temp[index++] = a[first1++];
        }
        else
        {
            ans += last1 - first1 + 1;
            temp[index++] = a[first2++];
        }
    }
    while(first1 <= last1)
    {
        temp[index++] = a[first1++];
    }
    while(first2 <= last2)
    {
        temp[index++] = a[first2++];
    }
    int i;
    for(i = first; i <= last; i++)
    {
        a[i] = temp[i - first];
    }
    delete [] temp;
    return;
}
void inv_pair(int *a, int first, int last)
{
    if(last - first > 0)
    {
        int mid = (last + first) / 2;
        inv_pair(a, first, mid);
        inv_pair(a, mid + 1, last);
        merge(a, first, mid, last);
    }
    return;
}
int main()
{
    int n, i;
    while(cin >> n)
    {
        ans = 0;
        for(i = 0; i < n; i++)
            cin >> a[i];
        inv_pair(a, 0, n - 1);
        cout << ans << endl;
    }
    return 0;
}

这里就一个操作可能不太好理解,对于从大到小排列数组a,如果a[first1]这个元素大于a[first2](形成一个逆序数对), 那么a[first1] 到 a[last1]的全部元素都会大于a[first2](形成 `last1-first1+1`个逆序数对)。

如图:

 

2019.2.20补充:

在上述方法中,计算逆序数时要统计的是从first1到last1的元素个数(last1-first1+1)。

那么,当a[first1]>a[first2]时,能不能统计从first2到(mid+1)的元素个数(first2-(mid+1)+1)呢?

答案是不行的,这样的话,可能会重复统计某些逆序对。

举例来说,当a[first1]>a[first2]时,我们统计了逆序对{(first1, mid+1), (first1, mid+2), ..., (first1, first2-1) 以及 (first1, first2) }。

现在假设a[first1]>a[first2+1],那么我们会统计逆序对{(first1, mid+1), (first1, mid+2), ..., (first1, first2-1), (first1, first2) 以及(first1, first2+1) }, 此时的统计跟之前有重复,这样统计出来的逆序数就比正确的值要大。

2018.2.21补充 迭代版本的计算逆序对

#include <iostream>
#include <vector>
using namespace std;
// 通过 https://vjudge.net/problem/HihoCoder-1524
unsigned long long merge_sort(vector<int>& arr){
    unsigned long long  cnt = 0;
    int n = arr.size();
    if(n <= 1) return 0;
    vector<int> tmp(n);
    int ls, le, rs, re, id;
    for(int i = 1; i < n; i <<= 1){
        for(int index = 0; index+i<n; index += (i<<1)){
            id = 0, ls = index, le = index+i-1, rs = le+1, re = min(le+i, n-1);
            while(ls <= le && rs <= re){
                if(arr[ls] <= arr[rs]) tmp[id++] = arr[ls++];
                else {
                    tmp[id++] = arr[rs++];
                    cnt += (le-ls+1);
                }
            } while(ls <= le){  // enter this loop when rs = re+1
                arr[--rs] = arr[le--];
            } while(id > 0){  // end this loop when id == 0
                arr[--rs] = tmp[--id];
            }
        }
    } return cnt;
}
void print(vector<int> v){
    for(int i = 0; i < v.size(); ++i){
        cout << v[i] << ' ';
    } cout << endl;
}
void check(vector<int> v){
    for(int i = 1; i < v.size(); ++i){
        if(v[i-1] > v[i]){
            cout << "sort failed" << endl;
            return;
        }
    } cout << "sort succeed" << endl;
}
void f(){
	/* 验证 */
    int n = 100, a[] = {364,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727,667,465,849,455,181,486,149,588,233,144,174,557,67,746,550,474,162,268,142,463,221,882,576,604,739,288,569,256,936,275,401,497,82,935,983,583,523,697,478,147,795,380,973,958,115,773,870,259,655,446,863,735,784,3,671,433,630,425,930,64,266,235,187,284,665,874,80,45,848,38,811,267,575};
    vector<int> v(a, a+n);
    int cnt = merge_sort(v);
    print(v);
    check(v);
    cout << "True answer is " << 2519 << endl;
    cout << "My answer is " << cnt << endl;
}
int arr[100000];
int main(){
    int n;
    while(cin >> n){
        for(int i = 0; i < n; ++i) cin >> arr[i];
        vector<int> v(arr, arr+n);
        cout << merge_sort(v) << endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值