zoukankan      html  css  js  c++  java
  • HDU 1394 Minimum Inversion Number(线段树/树状数组求逆序数)

    Minimum Inversion Number

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 17737    Accepted Submission(s): 10763

    Problem Description
    The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

    For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

    a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
    a2, a3, ..., an, a1 (where m = 1)
    a3, a4, ..., an, a1, a2 (where m = 2)
    ...
    an, a1, a2, ..., an-1 (where m = n-1)

    You are asked to write a program to find the minimum inversion number out of the above sequences.
     
    Input
    The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
     
    Output
    For each case, output the minimum inversion number on a single line.
     
    Sample Input
    10 1 3 6 9 0 8 5 7 4 2
     
    Sample Output
    16
     
    Author
    CHEN, Gaoli
     
    Source
     

    题目链接:HDU 1394

    主要操作:单点更新,区间求和

    通过此题学习到了如何用NlogN的时间来求一个数列的逆序对个数(简称逆序数),先上暴力求法

    暴力求法代码:

    int cnt=0;
    for (int i=1; i<=n; ++i)
    {
    	for (int j=i+1; j<=n; ++i)
    	{
    		if(arr[i]>arr[j])//i<j但arr[i]却大于arr[j],这就是一个逆序对<arr[i],arr[j]>
    			++cnt;
    	}
    }
    printf("%d
    ",cnt);//cnt即为逆序对个数

    那显然题目中的n可达5000,一平方估计得TLE,然后学习了一下用树状数组或线段树来求的方法。

    主要思路:按照顺序读入一个数,记为arr[i]吧,然后对【arr[i]+1, end】求和记为sum,为什么这样就可以得到arr[i]的逆序对呢?

    想想逆序对的定义:i<j且arr[i]>arr[j](假设i<j前面),显然题目按顺序读入已经无形中满足了i<j的要求,也就是说正常上升序列应该是i<j且arr[i]<arr[j] ,但是你出现了一个arr[i]>arr[j],也就是说让【arr[j],end】之间的arr[i]这个点的值发生变化(变为1),那我一旦对【arr[j]+1,end】求和就肯定会出现sum>0的情况,那这个大于0是谁造成的呢?就是某个大于arr[j]的arr[i],不用管他是谁,反正只要arr[i]>arr[j]就能对【arr[j],end】一段求和造成影响,那显然一个arr[i]与arr[j]能组成一对,那n个就组成n对,那每一个的影响值都设为1,那求和求出来的就是对数。

    然后得到了最初的逆序数怎么往后推?那就考虑开头的数移到末尾的影响,显然比开头的arr[0]小的数有arr[0]-1个记为low(题目中范围为0~n-1的自然数,且会全部出现且不重复),然后除去开头的数一共有n-1个,那么剩下比arr[0]大的数有(n-1)-(arr[0]-1)个即n-arr[0]个记为up。然后看开头的移到末尾,原本的逆序数变成了正序,原本的正序变成了逆序,因此移动之后的sum'=sum+(正序变为逆序就要加)up-(逆序变为正序就要减)low

    代入n与arr[0]得到sum'=sum+n-arr[0]-(arr[0]-1)=sum+n-arr[0]-arr[0]+1。

    最后值得注意的是用树状数组的话题目中范围是0~n-1,出现0会死循环,因此arr[i]要自增1再操作。

    树状数组代码(31ms):

    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<sstream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    #include<deque>
    #include<stack>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define CLR(x,y) memset(x,y,sizeof(x))
    #define LC(x) (x<<1)
    #define RC(x) ((x<<1)+1)
    #define MID(x,y) ((x+y)>>1)
    typedef pair<int,int> pii;
    typedef long long LL;
    const double PI=acos(-1.0);
    const int N=5010;
    int T[N];
    inline int lowbit(const int &n)
    {
    	return (n&-n);
    }
    void add(int k,int val)
    {
    	while (k<N)
    	{
    		T[k]+=val;
    		k+=lowbit(k);
    	}
    }
    int getsum(int k)
    {
    	int r=0;
    	while (k)
    	{
    		r+=T[k];
    		k-=lowbit(k);
    	}
    	return r;
    }
    int arr[N];
    int main(void)
    {
    	int n,i,j;
    	while (~scanf("%d",&n))
    	{
    		CLR(arr,0);
    		CLR(T,0);
    		int sum=0;
    		for (i=1; i<=n; ++i)
    		{
    			scanf("%d",&arr[i]);
    			++arr[i];
    			sum+=(getsum(n)-getsum(arr[i]));
    			add(arr[i],1);
    		}
    		int minm=INF;
    		for (i=1; i<=n; ++i)
    		{
    			sum=sum+(n-arr[i])-arr[i]-1);
    			if(sum<minm)
    				minm=sum;
    		}
    		printf("%d
    ",minm);
    	}
    	return 0;
    }

    线段树代码 (62ms):

    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<sstream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    #include<deque>
    #include<stack>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define CLR(x,y) memset(x,y,sizeof(x))
    #define LC(x) (x<<1)
    #define RC(x) ((x<<1)+1)
    #define MID(x,y) ((x+y)>>1)
    typedef pair<int,int> pii;
    typedef long long LL;
    const double PI=acos(-1.0);
    const int N=5010;
    struct info
    {
    	int l,r,mid;
    	int sum;
    }T[N<<2];
    void pushup(int k)
    {
    	T[k].sum=T[LC(k)].sum+T[RC(k)].sum;
    };
    void build(int k,int l,int r)
    {
    	T[k].sum=0;
    	T[k].l=l;
    	T[k].r=r;
    	T[k].mid=MID(l,r);
    	if(l==r)
    		return ;
    	build(LC(k),l,T[k].mid);
    	build(RC(k),T[k].mid+1,r);
    	//pushup(k);
    }
    void update(int k,int x,int val)
    {
    	if(T[k].l==T[k].r&&T[k].l==x)
    		T[k].sum+=val;
    	else
    	{
    		if(x<=T[k].mid)
    			update(LC(k),x,val);
    		else
    			update(RC(k),x,val);
    		pushup(k);
    	}	
    }
    int range_query(int k,int l,int r)
    {
    	if(l<=T[k].l&&r>=T[k].r)
    		return T[k].sum;
    	else
    	{
    		if(r<=T[k].mid)
    			return range_query(LC(k),l,r);
    		else if(l>T[k].mid)
    			return range_query(RC(k),l,r);
    		else
    			return range_query(LC(k),l,T[k].mid)+range_query(RC(k),T[k].mid+1,r);
    	}
    }
    int arr[N];
    int Scan()
    {
        int res=0,ch,flag=0;
        if((ch=getchar())=='-')
            flag=1;
        else if(ch>='0'&&ch<='9')
            res=ch-'0';
        while((ch=getchar())>='0'&&ch<='9')
            res=res*10+ch-'0';
        return flag?-res:res;
    }
    int main(void)
    {
    	int n,i,j;
    	while (~scanf("%d",&n))
    	{
    		int sum=0,minm=INF;
    		build(1,0,N);
    		for (i=0; i<n; ++i)
    		{
    			arr[i]=Scan();
    			sum+=range_query(1,arr[i]+1,N);
    			update(1,arr[i],1);
    		}
    		for (i=0; i<n; ++i)
    		{
    			sum=sum+((n-1)-arr[i])-arr[i];
    			if(sum<minm)
    				minm=sum;
    		}
    		printf("%d
    ",minm);
    	}
    	return 0;
    }
  • 相关阅读:
    【mysql】关于IO/内存方面的一些优化
    【mysql】使用tpcc-mysql进行压力测试
    Linux下使用iostat 监视I/O状态
    【JUC】JUC线程池框架综述
    【目录】JUC集合框架目录
    【JUC】JDK1.8源码分析之ConcurrentSkipListSet(八)
    【JUC】JDK1.8源码分析之CopyOnWriteArraySet(七)
    【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)
    【设计模式】策略模式
    【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)
  • 原文地址:https://www.cnblogs.com/Blackops/p/5766268.html
Copyright © 2011-2022 走看看