HDU 1394 Minimum Inversion Number【线段树&&归并排序】

来源:互联网 发布:社交软件 日语 编辑:程序博客网 时间:2024/06/09 16:47

Minimum Inversion Number

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 15156    Accepted Submission(s): 9253


Problem Description
The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.
 


Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
 


Output
For each case, output the minimum inversion number on a single line.
 


Sample Input
101 3 6 9 0 8 5 7 4 2
 


Sample Output
16



题目大意就是说,首先,逆序数就是ai > aj , i < j ,这样的情况。然后题意就是说给一数列,然后每次把第一个数移到最后可以得到一个新的数列,这样n个数的数列可得到n个数列,问这n个数列中最小的逆序对数。


然后是求逆序对数,但不用每个数列都求,求出一个之后,设第一个数为x,则数列后有x个数比它小,n-x-1个数比它大,现在把x放到最后则x之前有n-x-1个数比它大,所以此时的逆序对数就是之前求得的减去x(开始x后的x个比它小的数现在没有了)再加上n-x-1(之前x前没有比它大的数,现在有n-x-1个比它大的数,所以加上)。


这里用线段树求逆序数的话,每次找出序列中最大的元素放到它本来的位置,然后看此时该位置之前有没有比它大的数,有几个,逆序数就相应加几。所以这里要用结构体记录值和位置,值用来排序求逆序数,位置用来恢复原来顺序,求最小逆序数。

#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>#define maxn 5000+10using namespace std;int sum[maxn<<2];struct lnode{    int v,p;};lnode rec[maxn];int cmp1(lnode a,lnode b){    return a.v>b.v;}int cmp2(lnode a,lnode b){    return a.p<b.p;}void pushup(int o){    sum[o]=sum[o<<1]+sum[o<<1|1];}void build(int o,int l,int r){    sum[o]=0;    if(l==r)        return ;    int mid=(l+r)>>1;    build(o<<1,l,mid);    build(o<<1|1,mid+1,r);    pushup(o);}void update(int o,int l,int r,int aim){    if(l==r)    {        sum[o]++;        return ;    }    int mid=(l+r)>>1;    if(aim<=mid)        update(o<<1,l,mid,aim);    else        update(o<<1|1,mid+1,r,aim);    pushup(o);}int query(int o,int l,int r,int a,int b){    if(a<=l&&b>=r)    {        return sum[o];    }    int mid=(l+r)>>1;    int ans=0;    if(b<=mid)        ans+=query(o<<1,l,mid,a,b);    else if(a>mid)        ans+=query(o<<1|1,mid+1,r,a,b);    else    {        ans+=query(o<<1,l,mid,a,mid);        ans+=query(o<<1|1,mid+1,r,mid+1,b);    }    return ans;}int main(){    int n;    while(~scanf("%d",&n))    {        int tem=0,cnt=0;        build(1,1,n);        for(int i=0;i<n;++i)        {            scanf("%d",&rec[i].v);            rec[i].p=i+1;        }        sort(rec,rec+n,cmp1);        for(int i=0;i<n;++i)        {            update(1,1,n,rec[i].p);            if(rec[i].p!=1)               cnt+=query(1,1,n,1,rec[i].p-1);        }        sort(rec,rec+n,cmp2);        tem=cnt;        for(int i=0;i<n;++i)        {            tem+=n-1-rec[i].v-rec[i].v;            cnt=cnt<tem?cnt:tem;        }        printf("%d\n",cnt);    }    return 0;}




这里归并排序就不说了。之前做的归并排序的题有解释

#include<cstdio>#include<cstring>#define maxn 5000+10int cnt,a[maxn];int b[maxn],p[maxn];void Merge(int left,int mid,int right){    int n=mid,m=right;    int i=left,j=mid+1;    int k=0;    while(i<=n&&j<=m)    {        if(a[i]<=a[j])            p[k++]=a[i++];        else        {            p[k++]=a[j++];            cnt+=n-i+1;        }    }    while(i<=n)        p[k++]=a[i++];    while(j<=m)        p[k++]=a[j++];    for(int i=0;i<k;++i)        a[left+i]=p[i];}void mergesort(int left,int right){    if(left<right)    {        int mid=(left+right)>>1;        mergesort(left,mid);        mergesort(mid+1,right);        Merge(left,mid,right);    }}int main(){    int n;    while(~scanf("%d",&n))    {        cnt=0;        for(int i=0;i<n;++i)        {            scanf("%d",&a[i]);            b[i]=a[i];        }        mergesort(0,n-1);        int mini=cnt;        for(int i=0;i<n;++i)        {            cnt+=n-b[i]-1-b[i];            mini=mini<cnt?mini:cnt;        }        printf("%d\n",mini);    }    return 0;}



0 0