POJ 2299 Ultra-QuickSort

题意:给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。

题目本质就是求逆序对了,简单介绍一下。逆序对是指在序列{a0,a1,a2...an}中,若ai<aj(i>j),则(ai,aj)上一对逆序对。而逆序数顾名思义就是序列中逆序对的个数。例如: 1 2 3是顺序,则逆序数是0;1 3 2中(2,3)满足逆序对的条件,所以逆序数只有1; 3 2 1中(1,2)(1,3)(2,3)满足逆序对,所以逆序是3。由定义不能想象,序列n的逆序数范围在[0,n*(n-1)/2],其中顺序时逆序数为 0,完全逆序时逆序数是n*(n-1)/2。

可以利用归并排序时计算逆序个数,时间复杂度是nlog2n,而空间复杂度2n。 利用归并求逆序是指在对子序列s1和s2在归并时,若s1[i]>s2[j](逆序状况),则逆序数加上s1.length-i,因为s1中i后面的数字对于s2[j]都是逆序的。具体看注释吧。

归并算法基本思路

设两个有序的子文件放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1中,待合并完成后将R1复制回R[low..high]中。
合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置。合并时依次比较R[i]和R[j]的关键字,取关键字较小的记录复制到R1[p]中,然后将被复制记录的指针i或j加1,以及指向复制位置的指针p加1。
重复这一过程直至两个输入的子文件有一个已全部复制完毕(不妨称其为空),此时将另一非空的子文件中剩余记录依次复制到R1中即可。

PS : 网上还有线段树和树状数组过的。。 好空再好好学习下o(>﹏<)o。。


//POJ 2299 归并排序版 3896K	375MS
#include <stdio.h>
#include <string.h>
#define MID(X,Y) (((X)+(Y)) >> 1 )
#define N  500005
int a[N] ;
int temp[N] ;
double sum ;        //注意结果的范围,int不够 max = N*(N-1)/2 > INT_MAX

void Merge ( int left , int mid , int right )       //并归
{
    int index ;
    index = 0 ;
    int i , j ;
    i = left ;
    j = mid + 1 ;
    while ( i <= mid && j <= right )
    {
        if ( a[i] <= a[j] )
        {
            temp[index++] = a[i++] ;
        }
        else
        {
            temp[index++] = a[j++] ;
            sum += mid - i + 1 ;            //注意:a[i]后面的数字对于a[j]都是逆序的 
        }
    }
    while ( i <= mid )
    {
        temp[index++] = a[i++] ;
    }
    while ( j <= right )
    {
        temp[index++] = a[j++] ;
    }
    for ( int k = 0 ; k < index ; k ++ )         //归并完成后将结果复制到原输入数组 */
    {
        a[left+k] = temp[k] ;
    }
}

void Merge_Sort ( int left , int right )        //归并排序
{
    if ( left < right )
    {
        int mid ;
        mid = MID( left , right ) ;
        Merge_Sort ( left , mid ) ;
        Merge_Sort ( mid + 1 , right ) ;
        Merge ( left , mid , right ) ;
    }
}

int
main ( )
{
    int n ;
    while ( EOF != scanf ("%d" , &n ) )
    {
        if ( 0 == n )
        {
            break ;
        }
        for ( int i = 1 ; i <= n ; i ++ )
        {
            scanf ("%d" , & a[i] ) ;
        }
        sum = 0 ;
        memset ( temp , 0 , sizeof ( temp ) ) ;
        Merge_Sort ( 1 , n ) ;
        printf ("%.0f\n" , sum ) ;
    }
    return 0 ;
}

线段树版:

//POJ 2299 线段树版 17996K	1141MS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX(A,B) ((A)>(B)?(A):(B))
#define MIN(A,B) ((A)<(B)?(A):(B))
#define MID(A,B) (((A)+(B))>>1)
#define L(X) ((X)<<1)
#define R(X) (((X)<<1)|1)
#define MAXN 500050

struct node
{
    int left , right ;
    int val ;
}tree[MAXN<<2];

struct ss
{
    int val ;
    int id ;
}a[MAXN];

int n ;
int resort[MAXN] ;

int
cmp ( const void *a , const void * b )
{
    return -(((ss* )a)->val - ((ss*)b)->val) ;
}

void
Build_Tree ( int const l , int const r , int const root )
{
    tree[root].left = l ;
    tree[root].right = r ;
    tree[root].val = 0 ;
    if ( l < r )
    {
        int mid ;
        mid = MID ( l , r ) ;
        Build_Tree ( l , mid , L(root) ) ;
        Build_Tree ( mid + 1 , r , R(root) ) ;
    }
    return ;
}

void
Modify ( int const l , int const r , int const root , int const val )
{
    if ( l == tree[root].left && r == tree[root].right )
    {
        tree[root].val += val ;
    }
    else
    {
        int mid ;
        mid = MID ( tree[root].left , tree[root].right ) ;
        if ( r < mid )
        {
            Modify ( l , r , L(root) , val ) ;
        }
        else if ( l > mid )
        {
            Modify ( l , r , R(root) , val ) ;
        }
        else
        {
            Modify ( l , mid , L(root) , val ) ;
            Modify ( mid + 1 , r , R(root) , val ) ;
        }
    }
    return ;
}


int
Query ( int const x , int const root )
{
    if ( tree[root].left == tree[root].right )
    {
        return tree[root].val ;
    }
    else
    {
        int mid ;
        mid = MID ( tree[root].left , tree[root].right ) ;
        if ( x <= mid )
        {
            return Query ( x , L(root) ) + tree[root].val ;
        }
        else
        {
            return Query ( x , R(root ) ) + tree[root].val ;
        }
    }
}

void
Discretization ( )  //离散化
{
    int temp ;
    temp = a[1].val ;
    int p ;
    p = 1 ;
    for ( int i = 1 ; i <= n ; i ++ )
    {
        if ( a[i].val != temp )
        {
            a[i].val = ++p ;
            temp = a[i].val ;
        }
        else
        {
            a[i].val = p ;
        }
    }
    for ( int i = 1 ; i <= n ; i ++ )
    {
        resort[a[i].id] = a[i].val ;
    }
    return ;
}

int
main ( )
{
    while ( EOF != scanf ("%d" , & n ) )
    {
        if ( !n )
        {
            break ;
        }
        Build_Tree ( 1 , MAXN , 1 ) ;
        memset ( resort , 0 , sizeof ( resort ) ) ;
        double sum ;
        sum = 0 ;
        for ( int i = 1 ; i <= n ; i ++ )
        {
            scanf ("%d" , & a[i].val ) ;
            a[i].id = i ;
        }
        //  sort(a+1,a+n+1,cmp);
        qsort ( a + 1 , n , sizeof (a[0]) , cmp ) ;
        Discretization ( ) ;
        for ( int i = 1 ; i <= n ; i ++ )
        {
            sum += Query ( resort[i] , 1 ) ;
            Modify ( resort[i] , MAXN , 1 , 1 ) ;
        }
        printf ("%.0f\n" , sum ) ;
    }
    return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值