HDU-1394-Minimum Inversion Number
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意是给出n个数,求其逆序数,并每次将第一个数移至最后,再求其逆序数,求这n个排列中逆序数最小的一个
逆序数的简单定义:The inversion number of a givennumber sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i< j and ai > aj.
假设已求得一个排列的逆序数为sum,并且数组为a[n],现把第一个数移至末尾,因为0至n-1个数连续,比a[0]大的数将变成逆序,比a[0]小的数将不构成逆序,所以现在的逆序数为sum+=(比a[0]大的数—比a[0]小的数)
即sum+=(n-1-a[0]-a[0]) 即sum+=(n-1-2*a[0])
此时求原始排列的逆序数可用暴力的方法,即每个数都和前面的数比较
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int n,sum,i,j,ans;
int a[5005];
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d",&a[i]);
sum=0;
for(i=0;i<n;i++)
for(j=0;j<i;j++)
if(a[j]>a[i])
sum++;
ans=sum;
for(i=0;i<n;i++)
{
sum+=(n-1-2*a[i]);
if(sum<ans)
ans=sum;
}
printf("%d\n",ans);
}
return 0;
}
暴力的方法在求原始排列的逆序数时比较耗时,此题也可用线段树来做
1 3 6 9 0 8 5 7 4 2
依次插入,插入1时,(1,n-1]中出现的有v1=0
插入3时,(3,n-1]中出现的有v2=0
...
插入0时,(0,n-1]中出现的有v5=4
...
将v1至v10叠加即可
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 5005
int a[N];
struct cam
{
int x; //起点
int y; //终点
int val;
}list[N*4];
void build(int k,int x,int y) //建树
{
int mid;
list[k].x=x;
list[k].y=y;
list[k].val=0;
if(list[k].x==list[k].y)
return;
mid=(x+y)/2;
build(k<<1,x,mid);
build(k<<1|1,mid+1,y);
}
int find(int k,int x,int y) //(x,y]中比x大且出现的数
{
int mid;
if(list[k].x==x&&list[k].y==y)
return list[k].val;
mid=(list[k].x+list[k].y)/2;
if(x>mid)
return find(k<<1|1,x,y);
else if(y<=mid)
return find(k<<1,x,y);
else
return find(k<<1,x,mid)+find(k<<1|1,mid+1,y);
}
void update(int x,int k) //更新所有包含x的区间的val值
{
int mid;
list[k].val++;
if(list[k].x==list[k].y)
return;
mid=(list[k].x+list[k].y)/2;
if(x<=mid)
update(x,k<<1);
else
update(x,k<<1|1);
}
int main()
{
int i,n,sum,ans;
while(scanf("%d",&n)!=EOF)
{
build(1,0,n-1);
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=find(1,a[i],n-1);
update(a[i],1);
}
ans=sum;
for(i=0;i<n;i++)
{
sum+=(n-1-2*a[i]);
if(sum<ans)
ans=sum;
}
printf("%d\n",ans);
}
return 0;
}