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 ); } }