• 树状数组求逆序对


    1、什么是逆序数?
    2、用树状数组求逆序数的总数
             2.1该背景下树状数组的含义
             2.2如何使用树状数组求逆序数总数
             2.3 C++实现代码
    View Code

    1、什么是逆序数?

             在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序数的总数就是这个排列的逆序数。

    2、用树状数组求逆序数的总数

             2.1该背景下树状数组的含义

             我们假设一个数组A[n],当A[n]=0时表示数字n在序列中没有出现过,A[n]=1表示数字n在序列中出现过。A对应的树状数组为c[n],则c[n]对应维护的是数组A[n]的内容,即树状数组c可用于求A中某个区间的值的和。

             树状数组的插入函数(假设为 void insert(int i,int x) )的含义:在求逆序数这个问题中,我们的插入函数通常使用为insert( i , 1 ),即将数组A[i]的值加1 (A数组开始应该初始化为0,所以也可以理解为设置A[ i ]的值为1,即将数字i 加入到序列的意思 )。,同时维护c数组的值。

             树状数组中区间求和函数(假设函数定义为: int getsun(int i ) )的含义:该函数的作用是用于求序列中小于等于数字 i 的元素的个数。这个是显而易见的,因为树状数组c 维护的是数组A的值,则该求和函数即是用于求下标小于等于 i 的数组A的和,而数组A中元素的值要么是0要么是1,所以最后求出来的就是小于等于i的元素的个数。

             所以要求序列中比元素a大的数的个数,可以用i - getsum(a)即可( i 表示此时序列中元素的个数)。

             2.2如何使用树状数组求逆序数总数

             首先来看如何减小问题的规模:

             要想求一个序列 a b c d,的逆序数的个数,可以理解为先求出a b c的逆序数的个数k1,再在这个序列后面增加一个数d,求d之前的那个序列中值小于d的元素的个数k2,则k1+k2即为序列a b c d的逆序数的个数。

             举个例子加以说明:

      假设给定的序列为 4 3 2 1,我们从左往右依次将给定的序列输入,每次输入一个数temp时,就将当前序列中大于temp的元素的个数计算出来,并累加到ans中,最后ans就是这个序列的逆序数个数。

    序列的变化(下划线为新增加元素)

    序列中大于新增加的数字的个数

    操作

    { }

    0

    初始化时序列中一个数都没有

    {4 }

    0

    往序列中增加4,统计此时序列中大于4的元素个数

    {4 3 }

    1

    往序列中增加3,统计此时序列中大于3的元素个数

    {4 3 2}

    2

    往序列中增加2,统计此时序列中大于2的元素个数

    {4 3 2 1}

    3

    往序列中增加1,统计此时序列中大于1的元素个数

             当所有的元素都插入到序列后,即可得到序列{4 3 2 1}的逆序数的个数为1+2+3=6.

            

             2.3 C++实现代码如下:

    #include <iostream>
    #include <string>
    using namespace std;
    #define N 1010
    int c[N]; 
    int n;
    int lowbit(int i)
    {
        return i&(-i);
    }
    int insert(int i,int x)
    {
        while(i<=n){
            c[i]+=x;
            i+=lowbit(i);
        }
        return 0;
    }
    
    int getsum(int i)
    {
        int sum=0;
        while(i>0){
            sum+=c[i];
            i-=lowbit(i);
        } 
        return sum;
    }
    void output()
    {
        for(int i=1;i<=n;i++) cout<<c[i]<<" ";
        cout<<endl;
    }
    int main()
    {
        while(cin>>n){
            int ans=0;
            memset(c,0,sizeof(c));
            for(int i=1;i<=n;i++){
                int a;
                cin>>a;
                insert(a,1);
                ans+=i-insert(a);//统计当前序列中大于a的元素的个数
            }
            cout<<ans<<endl;
        }
        return 0;
    }

    ——来自 熊猫 [http://www.cnblogs.com/xiongmao-cpp/]

    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    使用C# 实现串口拨号器的SIM卡通信
    物联网协议Coap协议介绍
    C#实现简单的串口通信
    C#硬件访问(摄像头、麦克风)
    请问在电脑里PNP是什么意思啊?
    原码,反码,补码,及Java中数字表示方法
    3_PHP表达式_5_数据类型转换_类型强制转换
    3_PHP表达式_4_PHP运算符
    3_PHP表达式_5_数据类型转换_类型自动转换
    3_PHP表达式_3_有关变量或常量状态的函数
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/9500567.html
Copyright © 2020-2023  润新知