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