HDUOJ Minimum Inversion Number

本文介绍了一种使用线段树解决最小逆序数问题的方法。通过递推更新序列的逆序对个数,实现对不同序列变化时逆序对数量的有效计算。

Minimum Inversion Number

题目意思很明确,每次将序列的第一个元素放到序列尾,产生一个新序列,求出序列中逆序对的个数,一共有n个这种序列,输出其中最小的逆序对个数。先用线段树求解出最原始序列的逆序对的个数,然后递推。

对原始序列的逆序对的个数,在节点信息中添加一个某个区间元素的个数的变量,每次加入新元素的时候,统计当前情况下,其前面的比它大的元素的个数,然后在将元素插入到节点中。递推的时候,由于每次只是将头元素放到尾,所以对于序列中小于头元素的元素的个数都减1 ,同时当其放在尾的时候,它对应的逆序元素的个数为比它大的元素个数。程序如下:

/*
ID: csuchenan
PROG: hduoj 1394 Minimum Inversion Number
LANG: C++
*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define Mid(x , y) (x + y) / 2
#define MAXN 5005

struct Tree{

	int left  ;
	int right ;
	int ncount;
}tree[MAXN * 4] ;

void build(int left , int right , int layer) ;
int  query(int left , int right , int layer) ;
void update(int pos , int layer) ;

void work() ;
int n ;
int val[MAXN] ;

int main(int argc , char * argv[]){
	
	while(scanf("%d" , &n)!=EOF){	
		
		work() ;
	}
	
	return 0 ;
}

void work(){
	
	int i ;
	int nval ;
	
	int nsum ;
	int nmin ;
	
	nsum = 0 ;
	
	build(0 , n - 1 , 1) ;
	
	for(i = 1 ; i <= n ; i ++){
		
		scanf("%d" , &val[i]) ;
		
		nsum += query(val[i] , n - 1 , 1) ;		
		update(val[i] , 1) ;
	}
	
	nmin = nsum ;
	
	for(i = 1 ; i <= n ; i ++){
	
		nsum = nsum - val[i] + n - val[i] - 1 ;
		
		if(nmin > nsum){
			nmin = nsum ;
		}
	}
	
	printf("%d\n" , nmin) ;
	
	return ;
}

void build(int left , int right , int layer){
	
	tree[layer].ncount = 0    ;
	tree[layer].left = left   ;
	tree[layer].right = right ;
	
	if(left == right){
		return ;
	}
	
	int llayer = layer * 2 ;
	int rlayer = layer * 2 + 1 ;
	
	int mid = Mid(left , right) ;
	
	build(left , mid , llayer) ;
	build(mid + 1 , right , rlayer) ;
	
	return ;
}

int query(int left , int right , int layer){

	if(tree[layer].left == left && tree[layer].right == right){
		
		return tree[layer].ncount ;
	}
	
	int mid = Mid(tree[layer].left , tree[layer].right) ;
	
	int nlayer ;
	
	if(right <= mid){
		
		nlayer = layer * 2 ;
		return query(left , right , nlayer) ;
	}
	if(left > mid){
		
		nlayer = layer * 2 + 1 ;
		return query(left , right , nlayer) ;
	}
	
	nlayer = layer * 2 ;
	
	return query(left , mid , nlayer ) + query(mid + 1 , right , nlayer + 1) ;
}

void update(int pos , int layer){
	
	tree[layer].ncount ++ ;
	
	if(pos == tree[layer].left && pos == tree[layer].right){
		return ;
	}
	
	int mid = Mid(tree[layer].left , tree[layer].right) ;
	
	int nlayer = layer * 2 ;
	
	if(pos <= mid){
		update(pos , nlayer ) ;
		return ;
	}
	
	update(pos , nlayer + 1 ) ;

	return ;
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值