树的直径和重心

原文地址

树的直径

树的直径(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是否为重心。

struct CenterTree{
    int n;
    int ans;
    int siz;
    int son[maxn];
    void dfs(int u,int pa){
        son[u]=1;
        int res=0;
        for (int i=head[u];i!=-1;i=edges[i].next){
            int v=edges[i].to;
            if (v==pa) continue;
            if (vis[v]) continue;
            dfs(v,u);
            son[u]+=son[v];
            res=max(res,son[v]-1);
        }
        res=max(res,n-son[u]);
        if (res            ans=u;
            siz=res;
        }
    }
    int getCenter(int x){
        ans=0;
        siz=INF;
        dfs(x,-1);
        return ans;
    }
}Cent;


树的重心

 

还有一种方法,虽然不能保证求出来的一定是重心,但是能找到一个结点,而这个以这个结点为根的话,所有的子树的结点数量都不会超过总结点数的一半。

同样是先搜索出son[u],从某个假设的根开始向下找,如果有子结点v,使son[v]>son[u]/2,就以v作为新的根,重复执行,直到没有满足条件的子结点。

 

【POJ 1655 Balancing Act】

给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数,如果个数相同就选取编号最小的。

直接套模板可解,注意要取编号最小的根。


你可能感兴趣的:(树的直径和重心)