zoukankan      html  css  js  c++  java
  • Hdu 1394 Minimum Inversion Number、Poj 2299 UltraQuickSort

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394

    题意:求某种规定序列中的最小逆序数。

    递推比较最小那部分比较简单,就不说了。

    主要想说:求逆序数可以用构建线段树的方法来做。其实思想和计数排序的思想差不多。每次处理a[i]时,先统计一下已经被计数的前几个数的计数和。(比较的是值。)然后再更新这个计数和。这道题的数据范围和下标范围是一样的,所以可以直接做。

    详见代码:

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <map>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    #define Maxn 5005
    #define lx x<<1
    #define rx ((x<<1) + 1)
    #define MID ((l + r)>>1)
    int A[Maxn];
    int S[Maxn<<2];
    
    void pushUp(int x)
    {
        S[x] = S[lx] + S[rx];
    }
    void build(int l,int r,int x)
    {
        if(l == r)
        {
            S[x] = 0;
            return;
        }
        build(l,MID,lx);
        build(MID+1,r,rx);
        pushUp(x);
    }
    int query(int L,int R,int l,int r,int x)
    {
        int ans = 0;
        if(L<=l && r<=R) return S[x];
        if(L<=MID) ans += query(L,R,l,MID,lx);
        if(R>=MID+1) ans += query(L,R,MID+1,r,rx);
        return ans;
    }
    void update(int p,int d,int l,int r,int x)
    {
        if(l == r)
        {
            S[x] +=d;
            return;
        }
        if(p<=MID) update(p,d,l,MID,lx);
        else update(p,d,MID+1,r,rx);
        pushUp(x);
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif
        int n;
        int sum,ans,temp;
        while(scanf(" %d",&n)!=EOF)
        {
            sum = ans = 0;
            build(0,n-1,1);
            for(int i=0;i<n;i++)
            {
                scanf(" %d",&A[i]);
                temp = query(A[i],n-1,0,n-1,1);
                sum += temp;
                update(A[i],1,0,n-1,1);
            }
            ans = sum;
            //递推求解,每次A[i]都在首位,则分为大于它的部分和小于它的部分。
            for(int i=0;i<n;i++)
            {
                sum += n-1-A[i] - A[i];
                ans = min(ans,sum);
            }
            printf("%d\n",ans);
        }
        return 0;
    }
    


    但是如果数据范围和下标范围不是一个范围的话,我们就不能直接用这个方法 做了。。试想,如果一个数有最大可以有十亿,难道我们要开一个十亿大小的数组来标记么。。这显然不现实。所以我们要对数据进行离散话,毕竟数据的数量达不到那么大。比如说这道题:http://poj.org/problem?id=2299。也是求逆序数。数据的范围特别大。但是数量比较少。非常适合离散化。

    关于什么是离散话,参考:http://www.matrix67.com/blog/archives/108 和 http://baike.baidu.com/view/3392254.htm

    那么离散化+线段树我们就可以做这道题了(不要忘了用long long,int存储不下最终结果):

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <map>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    #define Maxn 500005
    #define lx x<<1
    #define rx ((x<<1) + 1)
    #define MID ((l + r)>>1)
    #define LL long long
    int rank[Maxn];
    LL S[Maxn<<2];
    
    struct Node
    {
        int val;
        int id;
        bool operator <(const Node &a) const
        {
            return val < a.val;
        }
    } A[Maxn];
    
    
    void pushUp(int x)
    {
        S[x] = S[lx] + S[rx];
    }
    void build(int l,int r,int x)
    {
        if(l == r)
        {
            S[x] = 0;
            return;
        }
        build(l,MID,lx);
        build(MID+1,r,rx);
        pushUp(x);
    }
    LL query(int L,int R,int l,int r,int x)
    {
        LL ans = 0;
        if(L<=l && r<=R) return S[x];
        if(L<=MID) ans += query(L,R,l,MID,lx);
        if(R>=MID+1) ans += query(L,R,MID+1,r,rx);
        return ans;
    }
    void update(int p,int d,int l,int r,int x)
    {
        if(l == r)
        {
            S[x] +=d;
            return;
        }
        if(p<=MID) update(p,d,l,MID,lx);
        else update(p,d,MID+1,r,rx);
        pushUp(x);
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif
        int n;
        LL ans = 0;
        int temp;
        while(scanf(" %d",&n)!=EOF)
        {
            ans = 0;
            if(n == 0) break;
            build(0,n-1,1);
            for(int i=0;i<n;i++)
            {
                scanf(" %d",&A[i].val);
                A[i].id = i;
            }
            sort(A,A+n);
            for(int i=0;i<n;i++) rank[A[i].id] = i;
            for(int i=0;i<n;i++)
            {
                temp = rank[i];
                ans += query(temp+1,n-1,0,n-1,1);
                update(temp,1,0,n-1,1);
            }
            printf("%lld\n",ans);
        }
        return 0;
    }
    

    另外,求逆序数也可以用树状数组和归并排序来做。

    用线段树属于平衡树做法的一种。

  • 相关阅读:
    rename 批量重命名
    shell脚本实现轮询查看进程是否结束
    mysql 修改max_connections
    window10下的solr6.1.0入门笔记之---安装部署
    php下载大文件
    【转】Pyhton 单行、多行注释符号使用方法及规范
    window10系统下使用python版本实现mysql查询
    Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A
    【Visual Studio】 使用EF、 Linq2Sql快速创建数据交互层(一)
    【OPCAutomation】 使用OPCAutomation实现对OPC数据的访问
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3122966.html
Copyright © 2011-2022 走看看