归并排序+归并排序求逆序对(例题P1908)

归并排序(merge sort)

顾名思义,这是一种排序算法,时间复杂度为O(nlogn),时间复杂度上和快排一样

归并排序是分治思想的应用,我们先将n个数不断地二分,最后得到n个长度为1的区间,显然,这n个小区间都是单调的,随后合并相邻的两个区间,得到n/2个单增(减)的区间,随后我们继续合并相邻的两个区间,得到n/4个单增(减)的区间....

每次合并操作的总时间复杂度为O(n),logn次合并用时O(logn),故总时间复杂度为O(nlogn)

合并操作比较好理解,就像下图这样二分区间即可(红线代表分割线):

然后,我们要如何实现O(n)的复杂度实现区间合并呢?

我们另开一个大小和原数组a大小一样的数组alt,存储需要合并的两个区间的数,方便起见,我们用pos代表alt数组的当前指向的位置,用i表示左区间当前所指的位置,用j表示右区间当前所指的位置,如下图所示:

记此时我们合并形成的区间为[l,r],按升序排序,那么我们枚举这一区间中的pos,每次比较alt[i]和alt[j],如果alt[i] < alt[j] 那么令a[pos] = alt[i],同时pos++,i++ ,否则令a[pos] = alt[j] ,同时pos++,j++,如果左区间的数已经全部遍历,那么将右区间剩下的数依次加入pos位置,反之同理,操作过程如下图所示:

  

至此,区间[l,r]这一段区间已经完成排序,这就是归并排序的合并过程

归并排序代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
const int Max = 5e5 + 10;

int n;
ll sum;
int alt[Max];

void merge(int a[], int l, int r)
{
    for(int i= l; i <= r; i ++)
    {
        alt[i] = a[i];
    }
    int mid = (l + r) >> 1;
    int i = l, j = mid + 1;
    for (int pos = l; pos <= r; pos++)
    {
        if (i == mid + 1)
        {
            a[pos] = alt[j];
            j++;
        }
        else if (j == r + 1)
        {
            a[pos] = alt[i];
            i++;
        }
        else if (alt[i] > alt[j])
        {
            a[pos] = alt[j];
            j++;
        }
        else
        {
            a[pos] = alt[i];
            i++;
        }
    }
}

void merge_sort(int a[], int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    merge_sort(a, l, mid);
    merge_sort(a, mid + 1, r);
    merge(a, l, r);
}

int a[Max];

int main()
{
#ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
#endif
    sum = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", a + i);
    merge_sort(a, 0, n - 1);
    for(int i = 0 ;i < n ;i ++)
        printf("%d%c",a[i],i == n-1?‘\n‘:‘ ‘);
    return 0;
}

利用归并排序求逆序对

我们注意到在归并排序过程中,我们有一步判断:if(alt[i] > alt[j]) ,如果判断为真,那么显然,j 和 区间[i,mid]每一个点都形成逆序对,一共mid-i+1个,而且只在这个地方会出现形成逆序对的情况,那么情况就很简单了,我们将原数组进行归并排序,并在if(alt[i] > alt[j] ) 为真的时候,统计一下逆序对的个数即可。

代码区

(点击此处查看模板题)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
const int Max = 5e5 + 10;

int n;
ll sum;
int alt[Max];

void merge(int a[], int l, int r)
{
    for(int i= l; i <= r; i ++)
    {
        alt[i] = a[i];
    }
    int mid = (l + r) >> 1;
    int i = l, j = mid + 1;
    for (int pos = l; pos <= r; pos++)
    {
        if (i == mid + 1)
        {
            a[pos] = alt[j];
            j++;
        }
        else if (j == r + 1)
        {
            a[pos] = alt[i];
            i++;
        }
        else if (alt[i] > alt[j])
        {
            a[pos] = alt[j];
            j++;
            sum += mid - i + 1;        //i及其此后的都可以和a[j]形成逆序对
        }
        else
        {
            a[pos] = alt[i];
            i++;
        }
    }
}

void merge_sort(int a[], int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    merge_sort(a, l, mid);
    merge_sort(a, mid + 1, r);
    merge(a, l, r);
}

int a[Max];

int main()
{
#ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
#endif
    sum = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", a + i);
    merge_sort(a, 0, n - 1);
    printf("%lld\n", sum);
    return 0;
}

原文地址:https://www.cnblogs.com/winter-bamboo/p/11449360.html

时间: 2024-11-06 11:17:06

归并排序+归并排序求逆序对(例题P1908)的相关文章

P1136 归并排序,求逆序对个数

这道题从看到它开始到做出来,已经过了快两周[因为第一次思路完全跑偏写的是暴力模拟想水过]: 题意是这样的:  jzabc除了对多米诺骨牌感兴趣外,对赛车也很感兴趣.上个周末他观看了一场赛车比赛.他总是能想出许多稀奇的问题.某一时刻,他看到有n辆车(总是匀速行驶)在同一直线上,并且处在一个无限长度的直道上,而且n辆车有严格的先后之分.他通过特殊的器材测出了每一辆车的速度.那么问题出现了,如果有两辆车A车和B车,A车在B车的后面,并且A车的速度比B车的快,那么经过一段时间后,A车一定会超过B车.我们

树状数组求逆序对

给定n个数,要求这些数构成的逆序对的个数.除了用归并排序来求逆序对个数,还可以使用树状数组来求解.树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0.从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了.把所有的加起来就是总的逆序对数.题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000.如果采用上面的思想,必然会导致空间的巨大浪费,而且由

分治法 求 逆序对数 的个数 时间复杂度为O(n*logn)

思路: 分治法 归并排序的过程中,有一步是从左右两个数组中,每次都取出小的那个元素放到tmp[]数组中 右边的数组其实就是原数组中位于右侧的元素.当不取左侧的元素而取右侧的元素时,说明左侧剩下的元素均比右侧的第一个元素大,即均能构成一个逆序对.假设现在左侧剩余n个元素,则逆序对数+n. 另外,如果当所有右侧的元素都取完,但是左侧仍然有元素剩余时,左侧剩余的元素已经在之前的运算中加到了逆序对中,不需要再添加一次 下面给出 归并排序 和 求逆序对数 两份代码: code1: 归并排序 #includ

求逆序对 36

? ? 引言 ? ? 一开始接触到这题还觉得挺有意思的,但后来发现其深层次的含义就是一个归并排序,只是在归并排序的过程中做了一点小动作而已,这也再次证明了很多东西都是万变不离其宗的 ? ? 本文首先讲了一下归并排序的过程,用了自己比较喜欢的简洁的方式,然后对比归并排序与求逆序对之间的关系,发现需要稍微修改一下合并两个已排序数组的方法,要从两个数组的最后的数开始 ? ? 最后考虑到如果求逆序对的字符是固定数目的话,比如4个,详情可以参考后面的,那么时间复杂度不用归并排序的nlogn了,如果我们可以

归并法求逆序对

l例题连接:https://www.luogu.org/problem/P1908 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目.Update:数据已加强. 输入格式 第一行,一个数n,表示序列中有n个数. 第

归并排序求逆序对

归并排序求逆序对 by mps [1]什么是逆序对? 对于一个数列需要按从小到大排序,如果有ai,aj且满足ai>aj和i<j则ai,aj为一组逆序对 [2]如何求逆序对? 我们发现,我们可以暴力枚举i,j,然后逐一判断并累加答案即可,时间复杂度O(N2)        但是对于数据量大一点的题目,只有不断地TLE了→_→ [3]归并排序求逆序对 逆序对的定义(见[1])是一组本应该有序的序列中的逆序对,那么我们就想到了排序,但由于是要22匹配,我们又想到了归并排序 归并排序大致内容如下: 将

HDU 3743 Frosh Week(归并排序求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3743 题目意思就是给你一个长为n的序列,让你求逆序对.我用的是归并排序来求的.归并排序有一个合并的过程,分前后两段,当a[i] > a[j]时,说明a[j]比前面那段啊[i],a[i+1],a[i+2]....,a[mid],比这些都要小,所以总逆序对数要加上mid-i+1. 1 // File Name: HDU3743.cpp 2 // Author: xiaxiaosheng 3 // Cre

归并排序,树状数组 两种方法求逆序对

我们知道,求逆序对最典型的方法就是树状数组,可是另一种方法就是Merge_sort(),即归并排序. 实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢? 我们能够这样考虑: 归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来. 在合并的过程中(设l<=i<=mid,mid+1<=j<=h).当a[i]<=a[j]时.并不产生逆序数:当a[i]>a[j]时.在 前半部分中比a[i]大的数都比a[j]

ZJNU 1247 归并排序求逆序对

逆序对——高级 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 118   Accepted: 28 Description 对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对.  例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个. 求n个数中的逆序对个数. Input 第一