zoukankan      html  css  js  c++  java
  • 树状数组求逆序对

    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/]

    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    Android 按键消息处理Android 按键消息处理
    objcopy
    SQLite多线程读写实践及常见问题总结
    android动画坐标定义
    Android动画效果translate、scale、alpha、rotate
    Android公共库(缓存 下拉ListView 下载管理Pro 静默安装 root运行 Java公共类)
    Flatten Binary Tree to Linked List
    Distinct Subsequences
    Populating Next Right Pointers in Each Node II
    Populating Next Right Pointers in Each Node
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/9500567.html
Copyright © 2011-2022 走看看