POJ 1241 Knockout Tournament 树形dp

http://poj.org/problem?id=1241

题意:有2^n运动员,两两分别进行一场比赛,胜的人可以继续比赛, 这样就可以形成一颗完全二叉树,求树中任意给定的一个结点的最好名次和最差名次。

思路: 树形dp ,一道比较经典的树形dp的题目,对于一个给定的结点,我们可以分别求出该结点的最好名次和最差名次。用low[i]和 high[i]分别表示i号结点的最差名次和最好名次,求最好名次的时候dp的方向是:根->叶子, 求最差名次的时候方向是:叶子->根。

最好名次;high[i] = high[j] + 1 其中j为i 的父亲,而且i和j 不是同一个运动员;

最差名次:low[i] = MA - ( MA - low[l_child]  + MA - low[r_child] + 1)  ;

代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define MAX(a,b) (b)>(b)?(a):(b)
int  n ;
int num[1<<10] ;
int low[1<<10] ;
int high[1<<10] ;
int MA ;

void DP1(int u){
	if(u >= (1<<n)){
		low[ num[u] ] = MA ;
		return ;
	}
	DP1(u<<1);	DP1((u<<1)+1);
	low[ num[u] ] =  MA - ( MA-low[num[u<<1]] + MA-low[num[(u<<1)+1]] + 1) ; 
}
void DP2(int u){
	if(u == 1){
		high[ num[u] ] = 1 ;
		return ;	
	}
	if(u & 1){
		DP2((u-1)>>1) ;	
		if(num[u] != num[(u-1)>>1]){
			high[ num[u] ] = high[ num[(u-1)>>1] ] + 1;
		}
	}	
	else{
		DP2(u>>1) ;	
		if(num[u] != num[u>>1]){
			high[ num[u] ] = high[ num[u>>1] ] + 1;	
		}
	}
}
int main(){
	int a,b;
	bool f = 1;
	while(scanf("%d",&n) == 1){
		if(n==0)	break ;
		for(int i=(1<<n),j=1;i<(1<<(1+n));i++,j++){
			num[i] = j ;	
		}	
		for(int c=n-1;c>=0;c--){
			for(int i=(1<<c) ;i<(1<<(c+1));i++){
				scanf("%d",&num[i]);	
			}
		}
		MA = 1<<n ;
		memset(low , 0 ,sizeof(low) );
		DP1(1);
		for(int i=(1<<n) ;i<(1<<(1+n));i++){
			DP2(i);	
		}
		scanf("%d",&a);
		if(!f){
			printf("\n");
		}
		f = 0 ;
		while(a--){
			scanf("%d",&b);	
			printf("Player %d can be ranked as high as %d or as low as %d.\n",b,high[b],low[b]);
		}
		
	}	
	return 0 ;	
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值