逆序对——归并排序

给定一个1-N的排列A1, A2, … AN,如果Ai和Aj满足i < j且Ai > Aj,我们就称(Ai, Aj)是一个逆序对。

求A1, A2 … AN中所有逆序对的数目。

Input
第一行包含一个整数N。

第二行包含N个两两不同整数A1, A2, … AN。(1 <= Ai <= N)

对于60%的数据 1 <= N <= 1000

对于100%的数据 1 <= N <= 100000

Output
一个整数代表答案

Sample Input
5
3 2 4 5 1
Sample Output
5

思路:

网上了解到要用归并排序(然而归并排序是个啥{{{(>_<)}}}),交换的次数就是最后的答案,学了两天,终于写出来了。注释还是蛮详细的。

下面是代码:

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<list>
#include<iterator>
#include<stack>
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
//#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
//#define max(a,b) (a)>(b)?(a):(b);
//#define min(a,b) (a)<(b)?(a):(b);
#define pi  acos(-1.0);
#define  eps 1e-9;
#define MAX 1000000+10
#include <queue>


void MergeSort(int ar[], int len, int sum[]);
void Merge(int left[], int lena, int right[], int lenb, int temp[]);

ull ans;
int a[100000];
int temp[100000];//记录每一次的段排序,减去了在link()函数中动态申请的时间

int main(void) {
    int n;

    while (sf("%d", &n) != EOF) {
        ans = 0;
        for (int i = 0; i < n; i++)  sf("%d", &a[i]);

        MergeSort(a, n, temp);//将a一分为二a[1]....a[9],a[10]......a[20]


        pf("%lld\n", ans);
    }

    /*system("pause");*/

    return 0;

}


void Merge(int left[], int left_len, int right[], int right_len,int temp[]) {
    /*
    函数功能:将排好序的left段和right段按排好序的状况连接,他们的状况保存在temp数组中
    */
    int i=0, j=0, k=0;
    while (i < left_len&&j < right_len) {

        if (left[i] > right[j])
            temp[k++] = right[j++], ans += left_len - i;//从left[i]到left[left_len-1]都可以和right[j]交换,因为left[i]到left[left_len]为升序排列
            //错误:temp[k++] = right[j++],ans++;只要right部分的往前添,就相当于交换,从右边交换到了左边
        else
            temp[k++] = left[i++];
    }

    //错误:if( i<left_len) ans = ans + (left_len - i-1) * right_len;//只要left部分的往后添,就相当于交换

    while (i < left_len) {
        temp[k++] = left[i++];

    }

    while (j < right_len)
        temp[k++] = right[j++];

    for (int i = 0; i < left_len + right_len; i++) {//将排序好的段重新输入原数组,因为之后temp数组还要一直变化,直到最后一次即21个元素都排好序时就和原数组一样了
        left[i] = temp[i];//之前没有将排序好的段输入数组中导致递归时temp数组被改动,无法得到正确结果
    }


}

void MergeSort(int ar[],int len,int sum[]) {
    if (len == 1)
        return ;

    else {

        int lenl = (len+1)>>1;
        int lenr = len - lenl;
        MergeSort(ar, lenl,sum);
        MergeSort(ar+lenl, lenr, sum+lenl);
        Merge(ar, lenl, ar+lenl, lenr, sum);

    }

}

下面是整理过的代码(其实也就是短了点儿,还是看上面吧):

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<list>
#include<iterator>
#include<stack>
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define max(a,b) (a)>(b)?(a):(b);
#define min(a,b) (a)<(b)?(a):(b);
#define pi  acos(-1.0);
#define  eps 1e-9;
#define MAX 1000000+10
#include <queue>
ull ans;
int a[100000];
int temp[100000];//记录每一次的段排序,减去了在Merge()函数中动态申请的时间

void Merge(int left[], int left_len, int right[], int right_len, int temp[]) {
    //函数功能:将排好序的left段和right段按排好序的状况连接,他们的状况保存在temp数组中
    int i = 0, j = 0, k = 0;

    while (i < left_len&&j < right_len) {
        if (left[i] > right[j])  temp[k++] = right[j++], ans += left_len - i;//从left[i]到left[left_len-1]都可以和right[j]交换,因为left[i]到left[left_len]为升序排列

        else  temp[k++] = left[i++];
    }

    while (i < left_len)
        temp[k++] = left[i++];

    while (j < right_len)
        temp[k++] = right[j++];

    for (int i = 0; i < left_len + right_len; i++) //将排序好的段重新输入原数组,因为之后temp数组还要一直变化,直到最后一次即21个元素都排好序时就和原数组一样了
        left[i] = temp[i];//之前没有将排序好的段输入数组中导致递归时temp数组被改动,无法得到正确结果

}

void MergeSort(int ar[], int len, int sum[]) {
    if (len == 1)  return;
    else {
        int lenl = (len + 1) >> 1;
        int lenr = len - lenl;
        MergeSort(ar, lenl, sum);
        MergeSort(ar + lenl, lenr, sum + lenl);
        Merge(ar, lenl, ar + lenl, lenr, sum);
    }
}

int main(void) {
    int n;
    while (sf("%d", &n) != EOF) {
        ans = 0;
        for (int i = 0; i < n; i++)  sf("%d", &a[i]);
        MergeSort(a, n, temp);//将a一分为二a[1]....a[9],a[10]......a[20]
        pf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jiruqianlong123/article/details/81562559