[HDU 1394]Minimum Inversion Number(归并排序/线段树)

来源:互联网 发布:学数据库之前学什么 编辑:程序博客网 时间:2024/06/05 09:23

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

Solution


先求这个排列的逆序对
然后每次把最前面的数字a[i]放到最后对之前求出的逆序对数inv的影响是inv=inv-a[i]+(n-1-a[i]) 因为比a[i]大的数有(n-1-a[i])个,比a[i]小的数有a[i]个
主要研究一下怎么nlogn求逆序对(数据太水了 其实暴力也能过

归并排序

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int a[5005],n,tmp[5005],inv,Min,num[5005];void mergesort(int l,int r){    if(l>=r)return;    int mid=(l+r)/2;    mergesort(l,mid);    mergesort(mid+1,r);    int i=l,j=mid+1,k=l;    while(i<=mid&&j<=r)    {        if(a[i]<=a[j])        {            tmp[k]=a[i];            i++;k++;        }        else        {            inv+=mid-i+1;            tmp[k]=a[j];            j++;k++;        }    }    while(i<=mid)    {        tmp[k]=a[i];        k++;i++;    }    while(j<=r)    {        tmp[k]=a[j];        k++;j++;    }    i=l;    while(i<=r)    {        a[i]=tmp[i];        i++;    }}int main(){    while(scanf("%d",&n)==1)    {        inv=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            num[i]=a[i];        }        mergesort(1,n);        Min=inv;        for(int m=1;m<n;m++)        {            inv=inv-num[m]+n-1-num[m];            if(Min>inv)Min=inv;        }        printf("%d\n",Min);    }    return 0;}

线段树

用线段树维护每个数字出现过没有
每插入一个,查询比它大的数字出现了几个

#include<iostream>#include<cstdio>#include<cstring>#define MAXN 5000using namespace std;int a[MAXN+5],n,inv,Min,res;struct Node{    int l,r,num;}segt[MAXN*4];void build(int idx,int a,int b){    segt[idx].l=a;    segt[idx].r=b;    segt[idx].num=0;    if(a==b)return;    build(idx*2,a,(a+b)/2);    build(idx*2+1,(a+b)/2+1,b);}void add(int idx,int a){    segt[idx].num++;    if(segt[idx].l==segt[idx].r)return;    int mid=(segt[idx].l+segt[idx].r)/2;    if(a<=mid)add(idx*2,a);    else add(idx*2+1,a);}void query(int idx,int a,int b){    if(segt[idx].l>=a&&segt[idx].r<=b)    {        res+=segt[idx].num;        return;    }    int mid=(segt[idx].l+segt[idx].r)/2;    if(b<=mid)query(idx*2,a,b);    else if(a>mid)query(idx*2+1,a,b);    else     {        query(idx*2,a,b);        query(idx*2+1,a,b);    }}int main(){    while(scanf("%d",&n)==1)    {        inv=0;        build(1,0,n-1);        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            add(1,a[i]);            res=0;            if(a[i]!=n-1)            query(1,a[i]+1,n-1);            inv+=res;        }        Min=inv;        for(int m=1;m<n;m++)        {            inv=inv-a[m]+n-1-a[m];            if(Min>inv)Min=inv;        }        printf("%d\n",Min);    }    return 0;}

树状数组

树状数组也可以的 但是没写

0 0
原创粉丝点击