本题两种解法,暴搜,线段树,线段树只是处理最初序列,找出最初的逆序数,之后每次移动为当前逆序数+比移动数大的数-比移动数小的数。
做本题时完全没有想法,完全没有~copy胡浩大神代码,第一次抄错题...然后抄完发现无法理解,耗了很久,最后再discuss里找到一位暴搜和线段树都过了。先看暴搜了解了思路。又慢慢看懂了线段树。
说说线段树找最初序列逆序数的思路:
先先建一个2^x>n的二叉树,每个节点初始化区间长度(感觉可以省略,不过初始化以后看起来方便点),区间逆序数为0.每输入一个数,查找数组中比它大的数的部分,加上其区间逆序数,然后将此树所属所有区间值更新(即+1)。
附暴搜代码,和线段树代码
#include<stdio.h>//暴搜
#include<string.h>
int s[5005];
int main()
{
int n,i,j,sum,f,t;
while(scanf("%d",&n)!=EOF)
{
memset(s,0,sizeof(s));sum=0;
for(i=0;i<n;i++)
scanf("%d",&s[i]);
for(i=0;i<n;i++)
{
t=0;
for(j=i+1;j<n;j++)
{
if(s[i]>s[j])t++;
}
sum=sum+t;
}
f=sum;
for(i=0;i<n;i++)
{
sum=sum+n-1-s[i]-s[i];
if(f>sum)f=sum;
}
printf("%d\n",f);
}
return 0;
}
#include<stdio.h>//线段树
#include<string.h>
#include<stdlib.h>
#define N 5005<<2
int a[5005];
int sum;
struct Node
{
int left;
int right;
int value;
};
struct Node node[N];
void build(int left,int right,int n)
{
int mid;
node[n].left=left;
node[n].right=right;
node[n].value=0;
if(node[n].left==node[n].right)return;
mid=(left+right)>>1;
build(left,mid,n<<1);
build(mid+1,right,n<<1|1);
}
void update(int index,int n)
{
int mid;
if(node[n].left==node[n].right)
{
node[n].value=1;
return ;
}
mid=(node[n].left+node[n].right)>>1;
if(index<=mid)update(index,n<<1);
else if(index>mid)update(index,n<<1|1);
node[n].value+=1;
}
void query(int l,int r,int n)
{
int mid;
if(node[n].left==l&&node[n].right==r)
{
sum+=node[n].value;
return ;
}
mid=(node[n].left+node[n].right)>>1;
if(r<=mid)query(l,r,n<<1);
else if(l>mid)query(l,r,n<<1|1);
else
{
query(l,mid,n<<1);
query(mid+1,r,n<<1|1);
}
}
int main()
{
int n,i,min;
while(scanf("%d",&n)!=EOF)
{
build(0,n-1,1);
sum=0;
for(i=0; i<n; i++)
{
scanf("%d",&a[i]);
query(a[i],n-1,1);//先查询再更新,这样不包括自己,或者从比自己大一的数开始找
update(a[i],1);
}
min=sum;
for(i=0;i<n;i++)
{
sum=sum+n-1-a[i]-a[i];
if(sum<min)min=sum;
}
printf("%d\n",min);
}
return 0;
}