Minimum Inversion Number
Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other)
Total Submission(s) : 26 Accepted Submission(s) : 20
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
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
该题目的意思 看了好久 ,最后参考博客才明白,就是把一个序列,
第一个 到n-1个 依次移到最后,全部移完之后,看有多少的最少的
逆序队。 解题思路大致这样, 累加该处到m的逆序对数目,然后,再
依次将1-n-1的数移到末尾。 根据减少的逆序对为 a【0】,增加的为m-a【0】+1;
最后 比较 求得最小的逆序对数目;
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#define maxn 5050
using namespace std;
int ss[maxn]; //定义 保存书
int a[maxn];
int lowbit(int x)
{
return x&(-x);
}
int sum(int x) //求和
{
int suM=0;
while(x>0)
{
suM+=ss[x];
x-=lowbit(x);
}
return suM;
}
void updata(int x,int y) //加减
{
while(x<maxn)
{
ss[x]+=y;
x+=lowbit(x);
}
}
int main()
{
int m,b,c;
while(~scanf("%d",&m))
{
memset(ss,0,sizeof(ss));
int ans=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
a[i]++;
ans+=sum(m)-sum(a[i]);
updata(a[i],1);
}
int min=ans;
for(int i=1;i<=m;i++)
{
ans+=m-a[i]-(a[i]-1);
if(ans<min) min=ans;
}
printf("%d\n",min);
}
return 0;
}
而本题也是好用 线段树来写的,先建立一棵线段树,然后输入a数组的值,
顺求得逆序数ans的初值,然后更新节点区域的值,注意子节点树之和为
父节点数,最后,,因为每次移动减少的是a【i】个逆序对,同时增加了
n-a【i】-1,,具体代码实现如下:
#include<stdio.h>
#include<math.h>
struct shu
{
int left;
int right;
int num;
}s[15010];
int n;
int a[5010];
int min(int a,int b)
{
return a>b?b:a;
}
void build(int ll,int rr,int i) //建立一棵线段树
{
s[i].right =rr;
s[i].left =ll;
s[i].num =0;
if(s[i].left==s[i].right)
{
return;
}
int mm;
mm=(s[i].left +s[i].right )/2;
build(ll,mm,2*i);
build(mm+1,rr,2*i+1);
}
int sum(int ll,int rr,int i) //求线段域的值
{
int ans=0;
if(ll==s[i].left &&rr==s[i].right )
return s[i].num;
int mm;
mm=(s[i].left+s[i].right)/2;
if(rr<=mm)
ans+=sum(ll,rr,2*i);
else if(ll>mm)
ans+=sum(ll,rr,2*i+1);
else
{
ans+=sum(ll,mm,2*i);
ans+=sum(mm+1,rr,2*i+1);
}
return ans;
}
void updata(int xx,int i) //
{
if(s[i].left ==s[i].right)
{
s[i].num ++;
return;
}
int mm=(s[i].left +s[i].right)/2;
if(xx<=mm) updata(xx,2*i);
else if(xx>=mm+1) updata(xx,2*i+1);
s[i].num=s[2*i].num +s[2*i+1].num; //父节点为两个节点的和
}
int main()
{
int i;
while(~scanf("%d",&n))
{
build(0,n-1,1);
int ans=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans+=sum(a[i],n-1,1); //初次求得逆序对
updata(a[i],1);
}
int tt=ans;
for(i=0;i<n;i++)
{
tt=tt+n-1-a[i]-a[i]; //移动引起的节点的增加减少
ans=min(ans,tt); //比较,求小的值
}
printf("%d\n",ans);
}
return 0;
}