原文地址
树的直径(Diameter)是指树上的最长简单路。
直径的求法:两遍搜索 (BFS or DFS)
任选一点w为起点,对树进行搜索,找出离w最远的点u。
以u为起点,再进行搜索,找出离u最远的点v。则u到v的路径长度即为树的直径。
简单证明:
如果w在直径上,那么u一定是直径的一个端点。反证:若u不是端点,则从直径另一端点到w再到u的距离比直径更长,与假设矛盾。
如果w不在直径上,且w到其距最远点u的路径与直径一定有一交点c,那么由上一个证明可知,u是直径的一个端点。
如果w到最远点u的路径与直径没有交点,设直径的两端为S与T,那么(w->u)>(w->c)+(c->T),推出(w->u)+(S->c)+(w->c)>(S->c)+(c->T)=(S->T)与假设矛盾。
因此w到最远点u的路径与直径必有交点。
S-----------c-----------T
|
w------u
树的重心:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
树的重心可以通过简单的两次搜索求出,第一遍搜索求出每个结点的子结点数量son[u],第二遍搜索找出使max{son[u],n-son[u]-1}最小的结点。
实际上这两步操作可以在一次遍历中解决。对结点u的每一个儿子v,递归的处理v,求出son[v],然后判断是否是结点数最多的子树,处理完所有子结点后,判断u是否为重心。
还有一种方法,虽然不能保证求出来的一定是重心,但是能找到一个结点,而这个以这个结点为根的话,所有的子树的结点数量都不会超过总结点数的一半。
同样是先搜索出son[u],从某个假设的根开始向下找,如果有子结点v,使son[v]>son[u]/2,就以v作为新的根,重复执行,直到没有满足条件的子结点。
给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数,如果个数相同就选取编号最小的。
直接套模板可解,注意要取编号最小的根。