原题链接
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
Author
CHEN, Gaoli
//http://acm.hdu.edu.cn/showproblem.php?pid=1394
/*
题意:给一个0-n-1的排列,这个排列中的逆序数为数对 (ai, aj) 满足
i < j and ai > aj的个数。依次把第一个数放到排列的末尾会得到另外n-1个排列,
求这n个排列中的最小的逆序数。
如果是0到n的排列,那么如果把第一个数放到最后,对于这个数列,
逆序数是减少a[i],而增加n-1-a[i]的.当然,你也可以是统计小于这个数的有多少个,
然后再用已经插入树中i个元素减去小于这个数的个数,得出的结果也是在一样的
//对于1 3 6 9 0 8 5 7 4 2这个数字串来说,逆序对就是
// 0 0 0 0 4 1 3 2 5 7,也就是对某一个数来说,前面有多少个数比他大他的逆序数就是多少
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define lson (id<<1)//这两个用法还是相当好用的
#define rson ((id<<1)|1)
//#define mid ((l+r)>>1)
using namespace std;
const int maxn=5000 + 5;
struct node{
int l,r;
int num,MAX;
}tree[(maxn<<2)];//这里的数组建为原来的四倍是有理论依据的
int a[maxn];
//为根节点为id的树根建树
void create(int id,int l,int r){
tree[id].l=l;tree[id].r=r;
tree[id].num=0;//由于这个题的限制所以这里要初始化为0
if(l==r) return;
int mid=(l+r)/2;
create(lson,l,mid);
create(rson,mid+1,r);
return;
}
//找到结点id所代表的区间的和
int getsum(int id,int l,int r){
if(l<=tree[id].l && tree[id].r<=r) return tree[id].num;
int sum=0;
int mid=(tree[id].l+tree[id].r)/2;
if(l<=mid)
sum+=getsum(lson,l,r);
if(r>mid)
sum+=getsum(rson,l,r);
return sum;
}
//更新这个num的结点的值(在本题中是要求更新为1的)
void updata(int id,int num){
if(tree[id].l==num && tree[id].r==num){
tree[id].num=1;
return;
}
int mid=(tree[id].l+tree[id].r)/2;
if(num<=mid)
updata(lson,num);
else
updata(rson,num);
tree[id].num=tree[lson].num + tree[rson].num;
return;
}
int main(){
int n;
while(scanf("%d",&n)==1&&n!=0){
create(1,0,n-1);
memset(a,0,sizeof(a));
int ans=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
ans+=getsum(1,a[i]+1,n-1);//得到前面有多少个数比他大
updata(1,a[i]);//然后这个数既然已经统计了,当然应该将这个数字放下,然后让后面的数字来计算逆序对的数中是否算上他
}
int minx=ans;
for(int i=0;i<n;i++){
ans=ans+n-2*a[i]-1;//这两个连续的序列之间存在着这样的一个关系
minx=min(minx,ans);
}
printf("%d\n",minx);
}
return 0;
}