http://poj.org/problem?id=1655
题意:一颗有N个结点的树, 每个结点的平衡度定义为:从树中去掉该结点之后形成的森林中结点最多的树的结点数 ,求平衡度最小的结点。如果有两个结点的平衡度相同,则输出编号较小的那个。
思路: 树形dp。 确定树的一个根结点,对于某个结点,首先用dp[i]表示以该结点为根节点的子树中结点的个数,在该结点的所有孩子的子树中的结点的个数由叶->根的顺序求出来之后,就可以确定该结点的平衡度了,balance[i] = max ( max(dp[j]) , N-sum(dp[j])-1 ),每个结点的平衡度都求出来之后,就可以找出最小的那个了。 这里还有一个问题需要注意,给定的树并没有给定具体的根和两个结点之间的父亲-孩子关系,只是单纯地给定了两个点之间的值。在找树的根的时候我们可以任意找一个结点开始(假设1),然后用dfs记录与每个结点相连的结点的关系(父亲or孩子),这样一颗树就建立起来了。
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#define MAX(a,b) a > b ? a : b ;
#define MAXN 20010
using namespace std;
int Tc ,N ;
vector<int> G[MAXN] ;
int f[MAXN] ;
int balance[MAXN] ;
inline void Init(){
for(int i=1;i<=N;i++){
G[i].clear() ;
f[i] = -1 ;
}
}
void build(int u){ //建树
for(int i=0;i<G[u].size();i++){
int v = G[u][i] ;
if(v == f[u]) continue ;
f[v] = u ;
build(v) ;
}
}
int dfs(int u){
int _max = 0 ,sum = 0 ;
for(int i=0;i<G[u].size();i++){
int v = G[u][i] ;
if(v == f[u]) continue ;
int res1 = dfs(v);
sum += res1 ;
_max = MAX(_max , res1 );
}
_max = MAX(_max ,N-sum-1);
balance[u] = _max ;
return sum + 1 ;
}
int main(){
int a ,b ;
scanf("%d",&Tc);
while(Tc--){
scanf("%d",&N);
Init();
for(int i=1;i<N;i++){
scanf("%d %d",&a,&b);
G[a].push_back(b) ;
G[b].push_back(a) ;
}
build(1) ;
dfs(1);
int _min = MAXN ,min_n;
for(int i=1;i<=N;i++){
if(_min > balance[i]){
_min = balance[i] ;
min_n = i ;
}
}
printf("%d %d\n",min_n ,_min);
}
return 0;
}