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.
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;
}