Description
Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1…N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the largest tree in the forest T created by deleting that node from T.
For example, consider the tree:
Deleting node 4 yields two trees whose member nodes are {5} and {1,2,3,6,7}. The larger of these two trees has five nodes, thus the balance of node 4 is five. Deleting node 1 yields a forest of three trees of equal size: {2,6}, {3,7}, and {4,5}. Each of these trees has two nodes, so the balance of node 1 is two.
For each input tree, calculate the node that has the minimum balance. If multiple nodes have equal balance, output the one with the lowest number.
Input
The first line of input contains a single integer t (1 <= t <= 20), the number of test cases. The first line of each test case contains an integer N (1 <= N <= 20,000), the number of congruence. The next N-1 lines each contains two space-separated node numbers that are the endpoints of an edge in the tree. No edge will be listed twice, and all edges will be listed.
Output
For each test case, print a line containing two integers, the number of the node with minimum balance and the balance of that node.
Sample Input
1
7
2 6
1 2
1 4
4 5
3 7
3 1
Sample Output
1 2
分析:
实际上这就是找树的重心
树的重心:
找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
树的重心可以通过简单的两次搜索求出,
第一遍搜索求出每个结点的子树大小son[u],
第二遍搜索找出使max{son[u],n-son[u]-1}最小的结点。
实际上这两步操作可以在一次遍历中解决。
对结点u的每一个儿子v,递归的处理v,求出son[v],
找到最大子树,处理完所有子结点后,判断u是否为重心。
还有一种方法,虽然不能保证求出来的一定是重心,但是能找到一个结点,而这个以这个结点为根的话,所有的子树的结点数量都不会超过总结点数的一半。
同样是先搜索出son[u],从某个假设的根开始向下找,如果有子结点v,使son[v]>son[u]/2,就以v作为新的根,重复执行,直到没有满足条件的子结点。
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int INF=0x33333333;
const int N=20010;
int n;
struct node{
int x,y,nxt;
};
node way[N<<1];
int st[N],tot,son[N],siz,ans;
bool p[N];
void add(int u,int w)
{
tot++;
way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].x=w;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
void dfs(int now,int fa)
{
son[now]=1;
int maxx=0;
for (int i=st[now];i;i=way[i].nxt)
{
int y=way[i].y;
if (y!=fa&&p[y])
{
p[y]=0;
dfs(y,now);
son[now]+=son[y];
maxx=max(maxx,son[y]); //找到最大的儿子
}
}
maxx=max(maxx,n-son[now]);
if (maxx<siz)
{
siz=maxx;ans=now;
}
else if (maxx==siz) ans=min(ans,now);
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
memset(st,0,sizeof(st));
tot=0;
for (int i=1;i<n;i++)
{
int u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
memset(son,0,sizeof(son));
memset(p,1,sizeof(p));
ans=0;
siz=INF;
p[1]=0;
dfs(1,0);
printf("%d %d
",ans,siz);
}
return 0;
}