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

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
  
  
10 1 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+10
using 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+10
int 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值