zoukankan      html  css  js  c++  java
  • POJ 2299 Ultra-QuickSort 求逆序数 (归并或者数状数组)此题为树状数组入门题!!!

    Ultra-QuickSort
    Time Limit: 7000MS   Memory Limit: 65536K
    Total Submissions: 70674   Accepted: 26538

    Description

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
    9 1 0 5 4 ,

    Ultra-QuickSort produces the output
    0 1 4 5 9 .

    Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

    Input

    The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

    Output

    For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

    Sample Input

    5
    9
    1
    0
    5
    4
    3
    1
    2
    3
    0
    

    Sample Output

    6
    0
    

    Source

     
    题目意思:
    对于给定的无序数组
    求除经过多少次相邻的元素交换之后,可以使得数组升序
    就是求一个数列的逆序数
     
     
    方法一:
    树状数组求解逆序数
    从头到尾读入这些数,每读入一个数就更新树状数组,
    查看它前面比它小的已出现过的有多少个数sum,
    然后用当前位置减去该sum,
    就可以得到当前数导致的逆序对数了。
    把所有的加起来就是总的逆序对数。
    code:
    #include<queue>
    #include<set>
    #include<cstdio>
    #include <iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define max_v 500005
    int n;
    struct node
    {
        int v;
        int pos;
    } p[max_v];
    int c[max_v];
    int re[max_v];
    int maxx;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int d)
    {
        while(x<=n)
        {
            c[x]+=d;
            x+=lowbit(x);
        }
    }
    int getsum(int x)
    {
        int res=0;
        while(x>0)
        {
            res+=c[x];
            x-=lowbit(x);
        }
        return res;
    }
    bool cmp(node a,node b)
    {
        return a.v<b.v;
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            if(n==0)
                break;
            memset(c,0,sizeof(c));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&p[i].v);
                p[i].pos=i;
            }
    
            sort(p+1,p+1+n,cmp);
            for(int i=1;i<=n;i++)
            {
                re[p[i].pos]=i;//离散化
            }
    
            long long ans=0;
            for(int i=1;i<=n;i++)
            {
                update(re[i],1);
                ans+=(i-getsum(re[i]));//当前位置减去前面比它小的数的个数之和就是答案
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /*
    题目意思:
    对于给定的无序数组
    求除经过多少次相邻的元素交换之后,可以使得数组升序
    就是求一个数列的逆序数
    
    
    从头到尾读入这些数,每读入一个数就更新树状数组,
    查看它前面比它小的已出现过的有多少个数sum,
    然后用当前位置减去该sum,
    就可以得到当前数导致的逆序对数了。
    把所有的加起来就是总的逆序对数。
    */

    感觉自己的树状数组这样写真的是傻的一批,离散化写的麻烦了,照着别人的离散化写的!

    现在明白了离散化是什么东西:就是数据范围压缩!!!

    贴一个自己离散化的代码

    #include<queue>
    #include<set>
    #include<cstdio>
    #include <iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define max_v 500005
    int n;
    struct node
    {
        int v;
        int pos;
    } p[max_v];
    int c[max_v];
    int re[max_v];
    int maxx;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int d)
    {
        while(x<max_v)
        {
            c[x]+=d;
            x+=lowbit(x);
        }
    }
    int getsum(int x)//返回1到x中小与等于x的数量
    {
        int res=0;
        while(x>0)
        {
            res+=c[x];
            x-=lowbit(x);
        }
        return res;
    }
    bool cmp(node a,node b)
    {
       if(a.v!=b.v)
         return a.v<b.v;
       else
        return a.pos<b.pos;
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            if(n==0)
                break;
            memset(c,0,sizeof(c));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&p[i].v);
                p[i].pos=i;
            }
    
            sort(p+1,p+1+n,cmp);
    
            long long ans=0;
            for(int i=1;i<=n;i++)
            {
                ans+=(i-getsum(p[i].pos)-1);//先找再更新,避免getsum的时候算上自己
                update(p[i].pos,1);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }

    方法二:归并排序求解逆序数

    在归并排序的过程中,比较关键的是通过递归,
    将两个已经排好序的数组合并,
    此时,若a[i] > a[j],则i到m之间的数都大于a[j],
    合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数,
    而小于等于的情况并不会产生。

    code:

    #include<stdio.h>
    #include<memory>
    #define max_v 500005
    typedef long long LL;
    LL a[max_v];
    LL temp[max_v];
    LL ans;
    void mer(int s,int m,int t)
    {
        int i=s;
        int j=m+1;
        int k=s;
        while(i<=m&&j<=t)
        {
            if(a[i]<=a[j])
            {
                temp[k++]=a[i++];
            }else
            {
                ans+=j-k;//求逆序数
                temp[k++]=a[j++];
            }
        }
        while(i<=m)
        {
            temp[k++]=a[i++];
        }
        while(j<=t)
        {
            temp[k++]=a[j++];
        }
    }
    void cop(int s,int t)
    {
        for(int i=s;i<=t;i++)
            a[i]=temp[i];
    }
    int megsort(int s,int t)
    {
        if(s<t)
        {
            int m=(s+t)/2;
            megsort(s,m);
            megsort(m+1,t);
            mer(s,m,t);
            cop(s,t);
        }
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            if(n==0)
                break;
            ans=0;
            for(int i=0;i<n;i++)
                scanf("%lld",&a[i]);
            megsort(0,n-1);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /*
    题目意思:
    对于给定的无序数组
    求除经过多少次相邻的元素交换之后,可以使得数组升序
    就是求一个数列的逆序数
    
    
    在归并排序的过程中,比较关键的是通过递归,
    将两个已经排好序的数组合并,
    此时,若a[i] > a[j],则i到m之间的数都大于a[j],
    合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数,
    而小于等于的情况并不会产生。
    */
     
  • 相关阅读:
    linux cmake安装方法
    Linux下安装numpy
    linux下安装opencv3.0
    linux升级gcc
    php session获取不到的解决方法
    python Tesseract安装方法
    SQLite-编译指示
    第十章:定积分
    7. 错误、调试和测试
    钱纳里的工业化阶段理论
  • 原文地址:https://www.cnblogs.com/yinbiao/p/9470624.html
Copyright © 2011-2022 走看看