题目描述
对于一个树T,其中n(1<=n<=20000)个节点编号为1…n。
从树中删除任何节点都会生成一个林:一个或多个树的集合。
将一个节点的平衡值定义为从T中删除该节点、创建的林中最大树的结点数。
例如,对于树:
删除节点4会生成两个树,其成员节点分别为5和1、2、3、6、7,这两棵树中较大的有五个节点,因此节点4的平衡值为5。
删除节点1会生成三棵大小相同的树的林:2、6,3、7和4、5,每个树都有两个节点,因此节点1的平衡值是2。
对于每个输入树,计算具有最小平衡值的节点。如果多个节点具有相等的平衡,则输出数字最小的节点。
输入格式
输入的第一行包含一个整数t(1<=t<=20),即测试用例的数量。
每个测试用例的第一行包含一个整数n(1<=n<=20000),接下来的n-1行每行包含两个空格分隔的节点号,这些节点号是树中一条边的端点,不会列出两次边,所有边都将列出。
输出格式
对于每个测试用例,打印一行包含两个整数,最小平衡值的节点编号 和 该节点的平衡值。
输入输出样例
输入样例1:复制
1 7 2 6 1 2 1 4 4 5 3 7 3 1
输出样例1:复制
1 2
【耗时限制】1000ms 【内存限制】64MB
问题目标是删除一个结点后,最大连通块的结点数最少,即找树的重心(质心)。
状态定义:任选一个节点为根,设d(i)表示以i为根节点的子树的节点个数。
状态转移方程: d(i)= sum(d(i)) ; j∈ son(i) ; 删除结点后最大连通块中结点数:f(i)= max(n-d(i),max (d(j));j∈son(i) ;
问题答案:ans=𝒎in(f(i))&&i编号最小。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <vector>
using namespace std;
int t,n,d[20005];
vector<int> G[20005];
int ans,id;
void dfs(int u,int fa){
int maxn=0;
d[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=fa){
dfs(v,u);
maxn=max(maxn,d[v]);
d[u]+=d[v];
}
}
maxn=max(maxn,n-d[u]);
if(maxn<ans||maxn==ans&&id>u) id=u,ans=maxn;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ans=0x3f3f3f3f;
id=0x3f3f3f3f;
memset(d,0,sizeof(d));
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
printf("%d %d\n",id,ans);
}
return 0;
}