hdu 1394 Minimum Inversion Number

题意描述:给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!

这题用暴力可以过,时间是125ms,最好的方法是用线段树,大概是50+ms

不过思想是一样的,首先统计初始的逆序数,因为题目总是将第一个数已到最后,所以原来比它小的数就不是逆序了,而原来比它大成为了逆序,显然在序列

0,1,2,3....n-1中

比a[i]小的数的个数为a[i]

比a[i]大的数的个数为n-a[i]+1

每次移动之后的逆序数sum就变为了

sum=sum-a[i]+n-a[i]-1;

把a[1],a[2]...a[n]都移动一遍,同时记录最小的逆序数,就行了。

暴力代码如下:

#include<stdio.h>
int a[5005];
int main()
{
 int n,sum,i,j,ans;
 while(scanf("%d",&n)!=EOF)
 {
  sum=0;
  for(i=1;i<=n;i++)
  {
   scanf("%d",&a[i]);
   for(j=1;j<i;j++)//统计初始的逆序数和
    if(a[j]>a[i])
     sum++;
  }
  ans=sum;
  for(i=1;i<=n;i++)//将所有的数都移动一遍,同时记录最小的逆序数
  {
      sum=sum-a[i]+(n-a[i]-1);
   if(ans>sum)
    ans=sum;
  }
  printf("%d\n",ans);
 }
 return 0;
}

 

下面介绍线段树的思想,这题中线段树用来统计初始的逆序数,其余的和暴力的差不多

先建一个空树,每输入一个数,就查询比它大的数的数目,然后更新树的value值(value是用来记录数的,每输入一个数,只要这个线段包含了这个数,value就加1),例如,序列3,2,4,1,5.逆序数,先输入3,查询得到sum=0,并让包含3的线段value都加1(也就是update(3,1)),再输入2时,查询2~5,之前只输入了3,sum=1,,之后更新update(2,1);

下面是线段树代码:

#include<stdio.h>

struct node{

       int left,right;

       int value;//记录插入数的个数

}a[5001*3];

int b[5001],sum=0;

void build(int s,int t,int step)//在s~t线段建树

{

       a[step].left=s;

       a[step].right=t;

       a[step].value=0;

       if(a[step].left==a[step].right)

              return ;

       int mid=(s+t)/2;

       build(s,mid,step*2);

       build(mid+1,t,2*step+1);

}

void query(int k,int t,int step)//在k~t查找

{

       if(a[step].left==k&&a[step].right==t)

       {

              sum+=a[step].value;

              return ;

       }

       int mid=(a[step].left+a[step].right)/2;

    if(mid>=t) query(k,t,2*step);

       else if(mid<k) query(k,t,2*step+1);

       else {

              query(k,mid,2*step);

              query(mid+1,t,2*step+1);

       }

}

void update(int k,int step)

{

       a[step].value++;

       if(a[step].left==a[step].right)

              return ;

       int mid=(a[step].left+a[step].right)/2;

       if(mid>=k) update(k,2*step);

       else update(k,2*step+1);

}

int main()

{

       int n,i,ans;

       while(scanf("%d",&n)!=EOF)

       {

              sum=0;

              build(0,n-1,1);

              for(i=1;i<=n;i++)

              {

                     scanf("%d",&b[i]);

                     query(b[i],n-1,1);

                     update(b[i],1);

              }

              ans=sum;

              for(i=1;i<=n;i++)

              {

                     sum=sum-b[i]+(n-b[i]-1);

                     if(ans>sum)

                            ans=sum;

              }

              printf("%d\n",ans);

       }

       return 0;

}

 

posted @ 2013-08-20 08:37  段少  阅读(138)  评论(0编辑  收藏  举报