HDU-1394 Minimum Inversion Number

  题目:给定一个数字串0 - N-1,每次可以移动首位数字到末尾,求最少的逆序对的组合方式。先求出原序列的逆序对,再用数学公式算出最佳值。

  假设有 10 个数,依题义为 0 - 9,那么首位为4的话,从首位移动到末尾产生新的逆序对为0 - 9中大于4的数字减去少于4的数。

  代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int N, num[5005];

void getint( int &t )
{
	char c;
	while( c= getchar(), c< '0'|| c> '9' ) ;
	t= c- '0';
	while( c= getchar(), c>= '0'&& c<= '9' )
	{
		t= t* 10+ c- '0';
	}
}

inline int get( int x )
{
	return N- 1- 2* x;
}

int main()
{
	while( scanf( "%d", &N )!= EOF )
	{ 
	    int cnt= 0;
		for( int i= 1; i<= N; ++i )
		{
		    getint( num[i] );
		    for( int j= 1; j< i; ++j )
		    {
		        if( num[i]< num[j] )
		        {
		            cnt++;
		        }
		    }
		}
		int min= cnt, temp= cnt;
		for( int i= 1; i<= N; ++i )
		{
		    temp+= get( num[i] );
		    if( temp< min )
		    {
		        min= temp;
		    }
		}
		printf( "%d\n", min );
	}
}

  

  下面的写法为线段树版,为什么要用线段树呢,注意上面的程序,在求逆序对的时候,我们都是暴力的遍历。现假设前面的数据中出现了某一区间的数都已经出现了,那么后面的数小于该区间的话,就可以之间处理了,不用一个一个的去比较,更像是把原先无序的数字,经过线段树变得具有一定的结构,便于后面的查询。

  代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int N, rec[5005];

struct Node
{
	int l, r, cnt;
}t[15005];

void creat( int p, int l, int r )
{
	t[p].l= l, t[p].r= r;
	t[p].cnt= 0; // 在创建一棵线段树的时候每个节点都应将所衔的数置零
	if( r- l> 1 )
	{
		creat( p<< 1, l, ( l+ r )>> 1 );
		creat( ( p<< 1 )+ 1, ( l+ r )>> 1, r );
	}
}

void update( int p, int l, int r, int &temp )
{ // 更新得到逆序对的最新数据
	if( t[p].l== l&&t[p].r== r )
	{
		temp+= t[p].cnt;
		return;
	}
	int mid= ( t[p].l+ t[p].r )>> 1;
	if( l>= mid )
	{
		update( ( p<< 1 )+ 1, l, r, temp );
	}
	else if( r<= mid  )
	{
		update( p<< 1, l, r, temp );
	}
	else
	{
		update( p<< 1, l, mid, temp );
		update( ( p<< 1 )+ 1, mid, r, temp );
	}
}

void add( int p, int pos )  // 添加操作,在经过的区间都加1
{
	t[p].cnt++;
	if( t[p].r- t[p].l== 1 )
	{
		return;
	}
	else if( pos>= ( t[p].l+ t[p].r )>> 1 )
	{
		add( ( p<< 1 )+ 1, pos );
	}
	else
	{
		add( p<< 1, pos );
	}
}

int get( int x )
{
	return N- 1- 2* x;
}

int main(  )
{
	while( scanf( "%d", &N )!= EOF )
	{
		int min= 0x7fffffff, temp= 0;
		for( int i= 1; i<= N; ++i )
		{
			t[i].cnt= 0;
		}
		creat( 1, 0, N+ 1 );  // 从0开始比编号
		for( int i= 1; i<= N; ++i )
		{
			scanf( "%d", &rec[i] );
			update( 1, rec[i]+ 1, N+ 1, temp ); // 寻找比rec[i]大的数
			add( 1, rec[i] );
		}
		for( int i= 1; i<= N; ++i )
		{
			temp+= get( rec[i] );
			if( temp< min )
			{
				min= temp;
			}
		}
		printf( "%d\n", min );
	}
}

  

posted @ 2011-08-15 21:52  沐阳  阅读(214)  评论(0编辑  收藏  举报