HDU 3743 Frosh Week(树状数组或归并排序求逆序)

HDU 3743 Frosh Week(树状数组或归并排序求逆序)

http://acm.hdu.edu.cn/showproblem.php?pid=3743

题意:

        给你一个n排列,每次你只能交换相邻的两个整数,现在要你用最少的交换次数使得该排列呈递增排列。要你输出需要交换的次数。

分析:

        首先这题类似于POJ2299:

http://blog.csdn.net/u013480600/article/details/21277047

        其实题目就是给你n个数的排列,然后问你这n个数的逆序和是多少.可以用归并排序做,也可以用树状数组左,其中用树状数组做的话,每读入一个a[i],当前树状数组中先查找已经出现的比a[i]大的数有多少,即执行temp=sum(n)-sum(a[i]),然后这个temp就是a[i]的逆序,执行ans+=temp;然后在标记a[i]值已经出现过一次了,执行add(a[i],1)即可.

        如果用归并排序做的话,参考刘汝佳的书.

AC代码:125ms归并排序

<span style="font-size:18px;">#include<cstdio>
using namespace std;
const int MAXN=500000+100;
int a[MAXN],b[MAXN];
long long merge_sort(int *a,int *b,int i,int j)//归并排序并返回逆序值,a为待排序的数组,b为辅助空间
{
    if(i==j)return 0;
    long long ans=0;//逆序值
    int mid= (i+j)/2;
    ans+=merge_sort(a,b,i,mid);//获取左边的逆序值
    ans+=merge_sort(a,b,mid+1,j);//获取右边的逆序值
    int p=i,q=mid+1,k=i;
    while(p<=mid||q<=j)//获取左边与右边关联的逆序值
    {
        if(p>mid || (q<=j&&a[p]>a[q]))
        {
            b[k++]=a[q++];
            ans+=mid-p+1;
        }
        else
        {
            b[k++]=a[p++];
        }
    }
    for(int k=i;k<=j;k++)a[k]=b[k];
    return  ans;

}

int main()
{
    long long ans;
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        ans = merge_sort(a,b,0,n-1);
        printf("%I64d\n",ans);
    }
    return 0;
}
</span>

AC代码:140ms,树状数组+离散化数据

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1000000+10;
int c[MAXN];
int lowbit(int x)
{
    return x&(-x);
}
int sum(int x)
{
    int res=0;
    while(x)
    {
        res +=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)
{
    while(x<MAXN)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
struct node
{
    int v;
    int index;
    bool operator <(const node &b)const
    {
        return v<b.v;
    }
}nodes[MAXN];
int a[MAXN];
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&nodes[i].v);
            nodes[i].index=i;
        }
        sort(nodes+1,nodes+n+1);
        int max_num=1;
        a[nodes[1].index]=1;
        for(int i=2;i<=n;i++)
        {
            if(nodes[i].v==nodes[i-1].v)
                a[nodes[i].index]=a[nodes[i-1].index];
            else
                a[nodes[i].index]=++max_num;
        }


        long long ans=0;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            int x=a[i];
            add(x,1);
            ans+= sum(n)-sum(x);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}</span>


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值