hdu 1394 Minimum Inversion Number(成段更新)

来源:互联网 发布:机械甩棍淘宝 编辑:程序博客网 时间:2024/06/07 02:57

题意:给你区间在[0,n-1]的整数,也就是n个数排成一行,下标从0开始到n-1,让你求它的逆序数之和,然后每次将最左边的的数移到末尾,重新求逆序数,这样有n组,让你求最小的逆序数之和,所谓逆序数,就是下标i<j,但数值确是x[i]>x[j],让你求这样的数有几个。

比如 : 3 1 2 4 0

下标比三小,数值比三大的数没有,则对三来说满足条件的是零个

然后考虑1,下标比一小,数值比一大,対一来说满足的为1个

同理 对2来说有一个,对四来说没有,对零来说有4个,所以逆序数个数为6个

这样一组逆序数就求出来了。

题目要求的是n组中逆序数的最小和,下面有一个规律:

每一次把第一个数移到最右边,原来比他小的数都构不成逆序数,比它大的数重新成为逆序数,所以要加上比它大的数,减去比他小的数,还有一个技巧,就是给定的是连续的数,假设第一个数值为m,要把它移到末尾,因为总共有n个数,下标从0 开始,所以,比m大的数就是(n-m-1)个,比m小的数就是m个,所以得到递推公式 sum =sum +(n-1+m)-(m),在用一重循环,求出最小的值。

两种方法:

第一种:暴力,两重循环,求出每一组逆序数之和

<span style="font-size:18px;">#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#define maxn 5555using namespace std;int x[maxn];int main(){    int n,sum,a;    while(~scanf("%d",&n))    {        sum=0;        for(int i=0;i<n;i++)              scanf("%d",&x[i]);        for(int i=n-1;i>0;i--)        {            for(int j=0;j<i;j++)               if(x[i]<x[j]) sum++; //每次循环找出比当前数下标比它小,数值比它大的数,次数加一        }      // printf("%d\n",sum);       int ans=sum;       for(int i=0;i<n;i++)           sum+=(-x[i])+(n-x[i]-1),ans=min(ans,sum);       printf("%d\n",ans);    }}</span><span style="font-size:24px;"></span>

第二种:线段树

输入一个点,就把他与之对应的叶子节点标记为1,说明这个点已经插入这样插入可以表明插入的先后,所以每次查询的只要查询当前点到n-1这段区间,若有标记为一的,就说明这是满足条件的数,逆序数和加一。

#include<iostream>#include<cstdio>#define maxn 5555#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;int sum[maxn<<2];int x[maxn];void pushup(int rt){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];  //向上更新逆序数的和}void build(int l,int r,int rt){   sum[rt]=0;   //初始化,建立一个空的线段树   if(l==r) return;   int m=(l+r)>>1;   build(lson);   build(rson);}int query(int L,int R,int l,int  r,int rt){    if(L<=l&&r<=R)  return sum[rt];    //每次询问,插入的点作为区间左端点,n-1作为区间右端点    int m=(l+r)>>1;    int ret=0;    if(L<=m) ret+=query(L,R,lson);    if(R>m)  ret+=query(L,R,rson);    return ret;}void update(int p,int c,int l,int r,int rt){    if(l==r)    {        sum[rt]=c;  //每次插入一个数,就标记为c        return ;    }    int m=(l+r)>>1;    if(p<=m) update(p,c,lson);    else update(p,c,rson);    pushup(rt);}int main(){    int n;    while(~scanf("%d",&n))    {        int sum=0;        build(0,n-1,1); //题意区间为[0,n-1]        for(int i=0;i<n;i++)  //每次输入一个点,先询问序号比他小,数值比他大的数的数数值和,就把他插入到线段树内        {            scanf("%d",&x[i]);            sum+=query(x[i],n-1,0,n-1,1); //询问            update(x[i],1,0,n-1,1);//更新        }        int ans = sum;        //printf("%d",ans);        for(int i=0;i<n;i++) //递推        {            sum+=(-x[i])+(n-x[i]-1);            ans=min(ans,sum); //不断求最小的值        }        printf("%d\n",ans);    }    return 0;}


0 0
原创粉丝点击