您的位置:首页 > 其它

HDU-1394 Minimum Inversion Number(线段树)

2017-08-10 15:48 357 查看
四处翻了翻解题报告,总结需解决两点

1、求初始序列逆序对数和。

这里可以使用线段树或者树状数组。

使用原理就是:利用线段树或者树状数组将 [0 - n-1]排开。

入读一个数时,

1)向后查询。例如读入3 , 那么就看看4 5 6 7 8 ….是否有之前输入过的。

然后累加起来 ,这便是初态的逆序对数。

2)单点更新,找到3这个点,标记一下 方便下次输入查询。

以线段树为例, 初始化线段树各个点sum[rt]=0 。读入一个数a[i]时

向后查询,s+=query(a[i],n-1,0,n-1,1); 前面两位参数是从a[i]向后查询的意思。

用s累加起来

再单点更新,使sum[a[i]]++。 update(a[i],1,0,n-1,1); 意思是从 [0,n-1]区间中 找到a[i] 这个点,进行+1操作

2、交换后的逆序对数, 跟上一个状态有关,s=s-a[i]+(n-1-a[i]); ;也就是这么一个公式带入具体数据推一推。

线段树实现代码如下:

#include <iostream>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 55555
using namespace std;
int sum[maxn<<2];
int cnt=1;
void PushUp(int rt)
{
sum[rt]=sum[rt*2]+sum[rt*2+1];
}
void build(int l,int r,int rt)
{
sum[rt]=0;
if(l==r)
return ;
int m=(l+r)/2;
build(lson);
build(rson);
PushUp(rt);
}
void update(int p,int add,int l,int r,int rt)///单点更新, 为了找到p点,用了二分
{
if(l==r)
{
sum[rt]+=add;
return;
}
int m=(l+r)/2;
if(p<=m) update(p,add,lson);
else update(p,add,rson);
PushUp(rt);///不忘回溯
}
int query(int L,int R,int l,int r,int rt)///查询某区间[L,R]的值
{
if(l==r) return sum[rt];
int m=(l+r)/2;
int ret=0;
if(L<=m) ret+=query(L,R,lson);///找到是【L,R】的子区间
if(R>m) ret+=query(L,R,rson);
return ret;
}

int main()
{
int n;
while(~scanf("%d",&n))
{
int  s=0;
int a[10000];
build(0,n+1,1);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
s+= query(a[i],n-1,0,n-1,1);
update(a[i],1,0,n-1,1);
}
int ans=s;
for(int i=1; i<n; i++)
{
s += n - a[i] - a[i] - 1;

ans=min(ans,s);
}
cout<<ans<<endl;
}

return 0;
}


个人理解一下使用线段树和树状数组区别,树状数组由于其特性 管理的数只能从 1开始。因此读入时每位i+1(如有错误请指出)

树状数组实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=5000;
int c[MAXN+1];
int lowbit(int x)
{
return x&(-x);
}
int sum(int x)
{
int res=0;
while(x>0)
{
res+=c[x];
x-=lowbit(x);
}
return res;
}
void add(int x,int v)
{
while(x<=MAXN)
{
c[x]+=v;
x+=lowbit(x);
}
}
int a[MAXN];
int main()
{
int n;
while(scanf("%d",&n)==1)
{
int counter=0;//计算逆序

memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]++;//标记
counter+= sum(n)-sum(a[i]);///读入是0 ,但是我们要的是1  。
add(a[i],1);
}
int ans=counter;//ans保存最终结果
for(int i=1;i<n;i++)
{
counter +=n-2*a[i]+1;
ans = min(ans,counter);
}
printf("%d\n",ans);
}
}


(如有错误,欢迎支持)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: