• HDU 1394 逆序数 线段树单点跟新 | 暴力


    Minimum Inversion Number

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 19575    Accepted Submission(s): 11756


    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个逆序数,从这里面选取最小的一个。
    思路:
    1.暴力
    将初始序列的逆序数求出来,之后的逆序数可以推导出来,假设得到了第一个逆序数 num。此时要将第一个数字data[i]放置到最后一个,那么num就要减去该数的值,重新产生了多少个逆序对呢?产生了n-data[i]-1个。就是序列中比data[i]大的数的个数。这个很容易推导出来。
    代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int maxn=50005;
    int ans[maxn];
    int main() {
        int n;
        while(~scanf("%d",&n)) {
            for(int i=0;i<n;++i) {
                scanf("%d",&ans[i]);
            }
            int sum=0;
            for(int i=0;i<n-1;++i) {
                for(int j=i+1;j<=n-1;++j) {
                    if(ans[i]>ans[j]) sum++;
                }
            }
            int result=sum;
            for(int i=0;i<n-1;++i) {
                sum=sum-ans[i]+(n-ans[i]-1);
                result=min(result,sum);
            }
            printf("%d
    ",result);
        }
        return 0;
    }
    

    2.用线段树来做。
    有了上面的推导,只要求出第一个,剩余的工作就变的简单了。
    再用线段树求原始序列的时候要反向思维,求每一个数之前的序列中比自身大的个数。
    假设现在给出一个序列是 : 4 3 1 5 0 2
    我们在一边度数据的过程中计算逆序对的个数。初始话每个线段树的结点值都赋值为0。
    先读到5,查询4-(n-1)区间即4-4有没有出现比4大的,没有,返回0。查询完成之后将该节点插入线段树。
    再读到3,查询3-(n-1)区间有没有比3大的,返回1,将3插入。再将3插入线段树。以此类推...这样我们得到如下结果:
    4  :  0
    3  :  1(4)
    1  :  2(3,4)
    5  :  0
    0  :  4(1,3,4,5)
    2  :  3(3,4,5)
    该序列的逆序数就是0+1+2+0+4+3=10。与我们求一般求逆序数时得到的结果相同。

    注意是0-n-1的序列,建树注意下标

    代码:
    #include<iostream>
    #include<string>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    using namespace std;
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    const int maxn=5005;
    int sum[maxn<<2];
    int data[maxn];
    void pushup(int rt) {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    void build(int l, int r, int rt) {
        sum[rt]=0;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(int val, int l, int r, int rt) {
        if(l==r) {
            sum[rt]++;return;
        }
        int mid=(l+r)>>1;
        if(val<=mid) update(val,lson);
        else update(val,rson);
        pushup(rt);
    }
    int query(int L, int R, int l, int r, int rt) {
        if(L<=l&&r<=R) {
            return sum[rt];
        }
        int mid=(l+r)>>1,cnt=0;
        if(L<=mid) cnt+=query(L,R,lson);
        if(R>mid) cnt+=query(L,R,rson);
        return cnt;
    }
    int main() {
        int n;
        while(~scanf("%d",&n)) {
            int result,temp=0;
            build(0,n-1,1);
            for(int i=0;i<n;++i) {
                scanf("%d",&data[i]);
                temp+=query(data[i],n-1,0,n-1,1);
                update(data[i],0,n-1,1);
            }
            result=temp;
            for(int i=0;i<n;++i) {
                temp=temp-data[i]+(n-1-data[i]);
                result=min(result,temp);
            }
            printf("%d
    ",result);
        }
        return 0;
    }
    


  • 相关阅读:
    修改 MySQL 的 sql_mode 模式方法
    PHP 实现 Redis 连接池
    【转载】php解决高并发问题
    PHP 7 不适用函数:password_hash
    PDO 防止 SQL 注入示例
    记录一次 header 参数格式引发的错误
    Laravel 框架数据库查询构造器中 when 的易犯错误
    PHP 7.3.4 安装 Redis 4.0(Windows系统)
    汇编语言全梳理(精简版)
    Anaconda安装和使用
  • 原文地址:https://www.cnblogs.com/lemonbiscuit/p/7775997.html
Copyright © 2020-2023  润新知