• HDU 1394 Minimum Inversion Number (树状数组)


    题目链接

    Problem Description

    The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

    For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

    a1, a2, ..., an-1, an (where m = 0 - the initial seqence)

    a2, a3, ..., an, a1 (where m = 1)

    a3, a4, ..., an, a1, a2 (where m = 2)

    ...

    an, a1, a2, ..., an-1 (where m = n-1)

    You are asked to write a program to find the minimum inversion number out of the above sequences.

    Input

    The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

    Output

    For each case, output the minimum inversion number on a single line.

    Sample Input

    10
    1 3 6 9 0 8 5 7 4 2
    

    Sample Output

    16
    

    题意

    给你n个数字 这些数字都是从0到n-1的,每次把第一个位置的数字移到最后一位形成一个新的序列,然后求出这些序列的逆序数,最后输出最小的逆序数。

    分析

    什么是逆序数:对于数x和y,若y的位置在x的前面并且y比x大,那么(x,y)就是一个逆序数对,

    普通方法解决:例子:

    5  4  3  6  1  2
    当输入5时,就用一个标记数组a[5]=1,(a数组初始化全部为零),然后从6开始往后遍历,当遇到a[i]==1时,说明i这个数在6之前出现过,那么6的逆序数对就增加一个。
    输入4时同理。。。。
    

    这种方法很容易想到,代码也很容易实现。两个for循环就行了,但是套路那么深,这道题怎么可能怎么简单,肯定会超时的,就得用树状数组来做了。

    设e[ ]就是这个树状数组,e数组是用来维护a数组的信息,e[i]代表是在当前情况下从1到i这个区间已经出现的数的个数,如 e[3]=2 代表的是在 [1,3]已经出现了两个数字 e[n]=k,代表的是在[1,n]已经出现的数的个数, e[n]-e[3]所代表的意思就很显然是此时大于3的已经出现的数的个数,这不就是3的逆序数嘛~~~我等凡夫俗子老半天才看懂大神的代码

    由于这道题的数据时从0到n-1的,当求出一个序列的逆序数后,把这个序列的第一个数放到最后的位置形成的新的序列,这个新的序列的逆序数和原来的逆序数是由一定的关系的:

    假设num[]数组如下  个数n=6
    1  4  3  5  0  2  这个序列的逆序数为  x
    4  3  5  0  2  1   当把1放到后面时,0的逆序数就减少了一个   但是比1 大的2 3  4  5  就成了1的逆序数
    3  5  0  2  1  4   当把4放到后面时,3,2,1,0的逆序数就各减少了一个   但是比4 大的5  就成了4的逆序数
    所以可推出如下规律: 新的逆序数t=x-num[1] + n-1-num[i]
    x-num[1]: 把第一个数字放到后面,比num[1]小的数字的逆序数减少了,
    + n-1-num[i]:把第一个数字放到后面,比num[1]大的数字就构成num[1]的逆序数
    

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int N=5010;
    
    int n,arr[N],num[N];
    
    int lowbit(int x)
    {
        return x&(-x);
    }
    
    void update(int id,int x)
    {
        while (id<=N)
        {
            arr[id]+=x;
            id+=lowbit(id);
        }
    }
    
    int Sum(int id)
    {
        int ans=0;
        while (id>0)
        {
            ans+=arr[id];
            id-=lowbit(id);
        }
        return ans;
    }
    int main()
    {
        //freopen("1.txt","r",stdin);   
        while (scanf("%d",&n)!=EOF)
        {
            memset(arr,0,sizeof(arr));
            int i,ans=0;
            for (i=1; i<=n; i++)
            {
                scanf("%d",&num[i]);
                ans+=Sum(n+1)-Sum(num[i]+1);//当前的逆序数个数
                update(num[i]+1,1);
            }
            int tmp=ans;
            for (i=1; i<=n; i++)
            {
                tmp+=n-1-num[i]-num[i];
                ans=min(ans,tmp);
            }
                printf("%d
    ",ans);
             
        }
        return 0;
    }
  • 相关阅读:
    DripRoad(点滴之路)
    如何写优雅的代码
    .Net 一直在改变
    Protobufnet的完美解决方案
    关于msgpack序列化后的消息包是否再压缩
    失眠
    创建一个比微软性能更好空间更少的GUID
    msgpack与protobuf的简单性能测试对比
    分布式游戏服务器的登陆流程
    对象池的实现与性能测试
  • 原文地址:https://www.cnblogs.com/cmmdc/p/6729642.html
Copyright © 2020-2023  润新知