树的重心
定义(百度百科)
树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。
性质
-
树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
-
把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
-
一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
-
一棵树最多有两个重心,且相邻。
算法流程
和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设d(i)表示以i为根的子树的结点的个数。不难发现d(i)=∑d(j)+1,j∈s(i)。s(i)为i结点的所有儿子结点的编号的集合。
程序也十分简单:只需要DFS一次,在无根树有根数的同时计算即可,连记忆化都不需要——因为本来就没有重复计算。
代码
#include<stdio.h> #include<stdlib.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) using namespace std; const int N=1000,INF=2147483647; int n,num_edge,ans_pos,ans_cnt=INF,head[N+1]; struct Edge{ int next,to; }edge[2*N]; inline void Add_edge(int from,int to) { edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge; } inline int max(int fa,int fb){return fa>fb?fa:fb;} inline int Dfs(int u,int fa) { int max_tree=0,pre_tree=1,psize; for(int i=head[u];i;i=edge[i].next) if(edge[i].to!=fa) psize=Dfs(edge[i].to,u),pre_tree+=psize,max_tree=max(max_tree,psize); max_tree=max(max_tree,n-pre_tree); if(max_tree<ans_cnt) { ans_cnt=max_tree; ans_pos=u; } return pre_tree; } int main() { int from,to; scanf("%d",&n); FORa(i,2,n) { scanf("%d%d",&from,&to); Add_edge(from,to),Add_edge(to,from); } Dfs(1,0); printf("%d %d",ans_cnt,ans_pos); return 0; } /*6 1 2 1 3 2 5 3 4 5 6*/