Minimum Inversion Number----hdu 1394

/********************************************************
Copyright:Glder
Author:Glder
Date:2013-07-31 14:40:29
Destription:
1、重点理解此题中使用的公式,首先求出原始数列每个数的逆序数a[i],
相加得出该数列总逆序数sum,然后数列中的元素每左移一次,sum的变化值为d
d = n-1-a[i]-a[i];
2、通过线段树求解时注意建树时向上更新语句的用法
3、注意程序中查询语句和更新语句使用的意义和方法
********************************************************/

/***************************************************
暴力求解法
注意公式使用方法:
首先求出原始数列每个数的逆序数a[i],相加得出该数列总逆
序数sum,然后数列中的元素每左移一次,sum的变化值为d
d = n-1-a[i]-a[i];
****************************************************/
int s[5010];
int a[5010];
int main()
{
    int n;
    int i,j;
    while(scanf("%d",&n) != EOF)
    {
        for(i = 0; i < n; i++)
            scanf("%d",&s[i]);


        int sum = 0,t;
        for(i = 0; i < n; i++)
        {
            t = 0;
            for(j = i+1; j < n; j++)
            {
                if(s[i] > s[j])
                    t++;
            }
            a[i] = t;
            sum += t;
        }
        int min = sum;
        for(i = 0; i < n-1; i++)
        {
            sum = sum - s[i] + n - 1 - s[i];
            if(sum < min)
                min = sum;
        }
        printf("%d\n",min);
    }
}


/***********************************************************
线段树求解法,仍需要利用暴力法中变化公式
************************************************************/
int min(int a,int b)
{
    return a<b ? a : b;
}

struct Segtree
{
    int left;
    int right;
    int num;
}s[5010*4];
void build(int root,int l,int r)
{
    s[root].left = l;
    s[root].right = r;

    //这一句必须放在下面if语句外面才可以,否则wa
    //原因:不同于直接输入时的建树方法,此时直接在该处的根节点标记为0就可以
    //如果下面的有向上更新语句的时候可以保证每个节点都可以得到更新才可以把
    //该语句放到if语句中。通过此题可以充分理解建树是向上更新语句的用法
    //s[root].num = 0;

    if(l == r)
    {
        s[root].num = 0;
        return ;
    }
    int m = (l + r) / 2;
    build(root<<1,l,m);
    build(root<<1|1,m+1,r);
    s[root].num = s[root*2].num + s[root*2+1].num;
}
void update(int root,int pos)
{
    int l = s[root].left;
    int r = s[root].right;


    if(l == r)
    {
        s[root].num++;
        return ;
    }
    int m = (l + r)/2;
    if(pos <= m)
        update(root*2,pos);
    else
        update(root*2+1,pos);
    s[root].num = s[root*2].num + s[root*2+1].num;
    //这一句必须加上,才能在更新是能够更新到父节点
}
int query(int root,int a,int b)
{
    int l = s[root].left;
    int r = s[root].right;


    if(a <= l && r <= b)
    {
        return s[root].num;
    }
    int m = (l + r) / 2;
    int ans = 0;
    if(a <= m)
        ans += query(root*2,a,b);
    if(b > m)
        ans += query(root*2+1,a,b);
    return ans;
}


int num[5010];
int main()
{
    int n;
    int i;
    while(~scanf("%d",&n))
    {
        build(1,0,n-1);
        int sum = 0;//计算该数列总逆序数
        //memset(num,0,sizeof(num));
        for(i = 0; i < n; i++)
        {
            scanf("%d",&num[i]);
            sum += query(1,num[i],n-1);//查询该数,如果之前有比之大的数,则总数++
            update(1,num[i]);//更新,在该数位置标记,表明该数已经存在
        }
        //cout<<sum<<endl;
        int ans = sum;
        for(i = 0; i < n; i++)
        {
            sum = sum + (n-1) - num[i] - num[i];
            ans = min(ans,sum);
        }
        printf("%d\n",ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值