Minimum Inversion Number

题目链接

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

题意

给你n个数字 这些数字都是从0到n-1的,每次把第一个位置的数字移到最后一位形成一个新的序列,然后求出这些序列的逆序数,最后输出最小的逆序数。

分析

什么是逆序数:对于数x和y,若y的位置在x的前面并且y比x大,那么(x,y)就是一个逆序数对,

普通方法解决:例子:

5  4  3  6  1  2
当输入5时,就用一个标记数组a[5]=1,(a数组初始化全部为零),然后从6开始往后遍历,当遇到a[i]==1时,说明i这个数在6之前出现过,那么6的逆序数对就增加一个。
输入4时同理。。。。

这种方法很容易想到,代码也很容易实现。两个for循环就行了,但是套路那么深,这道题怎么可能怎么简单,肯定会超时的,就得用树状数组来做了。

设e[ ]就是这个树状数组,e数组是用来维护a数组的信息,e[i]代表是在当前情况下从1到i这个区间已经出现的数的个数,如 e[3]=2 代表的是在 [1,3]已经出现了两个数字 e[n]=k,代表的是在[1,n]已经出现的数的个数, e[n]-e[3]所代表的意思就很显然是此时大于3的已经出现的数的个数,这不就是3的逆序数嘛~~~我等凡夫俗子老半天才看懂大神的代码

由于这道题的数据时从0到n-1的,当求出一个序列的逆序数后,把这个序列的第一个数放到最后的位置形成的新的序列,这个新的序列的逆序数和原来的逆序数是由一定的关系的:

假设num[]数组如下  个数n=6
1  4  3  5  0  2  这个序列的逆序数为  x
4  3  5  0  2  1   当把1放到后面时,0的逆序数就减少了一个   但是比1 大的2 3  4  5  就成了1的逆序数
3  5  0  2  1  4   当把4放到后面时,3,2,1,0的逆序数就各减少了一个   但是比4 大的5  就成了4的逆序数
所以可推出如下规律: 新的逆序数t=x-num[1] + n-1-num[i]
x-num[1]: 把第一个数字放到后面,比num[1]小的数字的逆序数减少了,
+ n-1-num[i]:把第一个数字放到后面,比num[1]大的数字就构成num[1]的逆序数

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=5010;

int n,arr[N],num[N];

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

void update(int id,int x)
{
    while (id<=N)
    {
        arr[id]+=x;
        id+=lowbit(id);
    }
}

int Sum(int id)
{
    int ans=0;
    while (id>0)
    {
        ans+=arr[id];
        id-=lowbit(id);
    }
    return ans;
}
int main()
{
    //freopen("1.txt","r",stdin);
     
    while (scanf("%d",&n)!=EOF)
    {
        memset(arr,0,sizeof(arr));
        int i,ans=0;
        for (i=1; i<=n; i++)
        {
            scanf("%d",&num[i]);
            ans+=Sum(n+1)-Sum(num[i]+1);//当前的逆序数个数
            update(num[i]+1,1);
        }
        int tmp=ans;
        for (i=1; i<=n; i++)
        {
            tmp+=n-1-num[i]-num[i];
            ans=min(ans,tmp);
        }
            printf("%d\n",ans);
         
    }
    return 0;
}

posted @ 2017-04-14 13:23  -梦里不知身是客  阅读(202)  评论(0编辑  收藏  举报