poj 2299(逆序对(树状数组||归并排序))

题目链接http://poj.org/problem?id=2299

题目大意:
在这个问题中,你必须分析一个特定的排序算法。 该算法通过交换两个相邻的序列元素来处理n个不同整数的序列,直到序列按升序排序。 对于输入序列
9 1 0 5 4,
通过Ultra-QuickSort输出
0 1 4 5 9。
你的任务是确定Ultra-QuickSort需要执行多少交换操作才能排序给定的输入序列。

输入
该输入包含几个测试用例。 每个测试用例从一行包含单个整数n <500,000 - 输入序列的长度开始。 以下n行中的每一行包含第i个输入序列元素的单个整数0≤a[i]≤999,999,999。 输入由长度为n = 0的序列终止。该序列不能被处理。

输出
对于每个输入序列,您的程序将打印一行包含一个整数op,一个排序给定输入序列所需的最小交换操作数。

样例输入
5
9
1
0
5
4
3
1
2
3
0

样例输出
6
0

参考博客http://blog.csdn.net/SeasonJoe/article/details/50193789?locationNum=15&fps=1

解法一:树状数组
由于题目数据过大,显然考虑离散化
然后用树状数组维护
可以将原数组位置与排序后的位置对应
以排序后数组建树状数组,
如果第i位有一个数

void update(int i)
{
    for(;i<=n;i+=lowbit(i))
    {
         c[i]++;
    }
}

对于每个数,我们搜它前面比它小的数的个数
再用它原数组位置减去即是包含这个数的逆序对个数
具体看代码

#include<bits/stdc++.h>
#define ll long long
#define MAX  2147483647
#define fi first
#define se second
#define mid (l+r>>1)
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fodown(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
int n;
struct node{
    int val,rank;
}a[500001];
int ch[500001];
int c[500005];
ll ans=0;
int read()
{
   int x=0,tmp=1;
   char ch=getchar();
   while(ch<'0' || ch>'9' ) { if(ch=='-') tmp=-1;ch=getchar();}
   while(ch>='0'&& ch<='9') { x = x * 10+ ch-'0';ch=getchar();}
   return x*tmp;
}

bool cmp(node a,node b)
{
    return a.val<b.val;
}

int lowbit(int x)
{
    return x&(-x);
}


void update(int x)//在X位置上有一个数
{
    for(;x<=n;x+=lowbit(x))
    {
        c[x]+=1;
    }   
}

int getsum(int x)
{
    ll sum=0;
    while(x>=1)
    {
        sum+=c[x];
        x-=lowbit(x);
     }  
     return sum;
}

int main()
{
  // freopen(" .in","r",stdin);
  // freopen(" .out","w",stdout);
   while(scanf("%d",&n)&&n)
   {
       fo(i,1,n)
       {
           a[i].val=read();
           a[i].rank=i;//离散化
       }
       sort(a+1,a+n+1,cmp);
       fo(i,1,n)
       ch[a[i].rank]=i;
       memset(c,0,sizeof(c));
       ans=0;
       fo(i,1,n)   
       {
          update(ch[i]);
          ans+=i-getsum(ch[i]);
       }
       cout<<ans<<endl;
   }


   return 0;
}

解法二:归并排序

显然,对于本题树状数组过于高大上了,数据范围大,难求前缀和,一定要离散化,复杂度应该比归并略高
如何想到归并?
题目要你想一个算法,求出将原数组排序后交换的次数
首先想到冒泡排序,但代价太高
快排又不行
O(nlog(n))算法只有归并可行(归并我只会求逆序对)
具体看代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define MAX  2147483647
#define fi first
#define se second
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fodown(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
int n,a[500005],b[5000005];
ll ans;
int read()
{
   int x=0,tmp=1;
   char ch=getchar();
   while(ch<'0' || ch>'9' ) { if(ch=='-') tmp=-1;ch=getchar();}
   while(ch>='0'&& ch<='9') { x = x * 10+ ch-'0';ch=getchar();}
   return x*tmp;
}


void merge(int l,int m,int r)
{
    int i=l,j=m+1,tot=l-1;
    while(i<=m&&j<=r)
    {
        if(a[i]<=a[j]) b[++tot]=a[i],i++;
        else
        {
            b[++tot]=a[j];
            ans+=m-i+1;/*我们能保证(l,m)这个区间是升序的,这是显然的,因为先排序这个区间,再排序整个区间,如果a[j]<a[i],那么a[j]与a[i]是逆序对,和a[i]后面的数(a[i~m])也是逆序对,因此个数加上m-i+1*/
            j++;
        }
    }
    while(i<=m)
    {
        b[++tot]=a[i];
        i++;
    }
    while(j<=r)
    {
        b[++tot]=a[j++];
    }
    fo(t,l,r)
    a[t]=b[t];
} 
void qsort(int l,int r)
{
    if(l==r) return ;
    int mid=l+r>>1;
    qsort(l,mid);//分块
    qsort(mid+1,r);
    merge(l,mid,r);
}

int main()
{
  // freopen(" .in","r",stdin);
  // freopen(" .out","w",stdout);
   while(scanf("%d",&n)!=EOF&&n)
   {
      fo(i,1,n) a[i]=read();
      ans=0;
      qsort(1,n); 
      cout<<ans<<endl;
   }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值