zoukankan      html  css  js  c++  java
  • 树的重心

    树的重心

    定义

    如果树上的某一个节点的最大子树的节点数最小,那么这个节点就是树的重心。

    性质

    1. 删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
    2. 树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
    3. 如果两棵树通过一条边合并,新的重心在原树的两个重心的路径上;
    4. 树删除或添加一个叶子节点,重心最多只移动一条边;

    树重心的求法

    从定义出发找重心:这个点作为根时,它的最大子树的节点个数不能大于树的全部节点数的一半。

    寻找一棵无根树的重心时,一般就先随便找一个点作为根,然后从这个点开始往下走dfs,然后整个dfs走完了之后,就能够找到树的重心了。

    本质上是这样的,假设现在有一棵无根树,我们先随便选点1作为根,那么整个树的结构是这样的

    image-20210206173334344

    一般来说我们认为以3、4、5为根节点的三棵子树就是节点2所拥有的三棵子树,但是这个情况是我们选择1作为根节点形成的树结构,如果是下面这种情况,那么以1为根的子树也算是节点2之下的

    image-20210206173435943

    所以,对于一个节点,需要从它下面的和“上面”的子树中,找出包含节点数最多的子树,并且它的节点数不能超过N/2,这样的话这个点就是树的重心了。“上面”的子树节点咋找呢?用节点总数减掉“下面”的子树节点数再减掉1(所讨论的该节点)即可。

    所以用数组s表示跑dfs的时候某个点与其下面的子树的节点和,而用数组w表示某个节点的最大子树所含的节点数。

    代码模板

    const int maxn = 15000;
    vector<int> G[maxn]; //使用vector邻接表来存图
    int cnt=0,s[maxn],w[maxn],centroid[maxn] = {0}; 
    
    void dfs(int cur,int fa) {
    	int siz = G[cur].size();
    	s[cur] = 1;
    	w[cur] = 0;
    	for(int i=0;i<siz;i++){  //遍历当前节点cur的所有相邻节点
    		int v = G[cur][i];
    		if(v != fa) {
    			dfs(v,cur);
    			s[cur] += s[v];   //dfs返回的时候,s[cur]会加上它下方所有子树的节点数
    			w[cur] = max(w[cur], s[v]); //w的值就是它下方最大子树的节点数
    		}
    	}
    	w[cur] = max(w[cur], N - s[cur]);  //如果w的值比它上方的子树小,则再更改w的值。
    	if( w[cur] <= N/2 )   //如果该层的w满足要求,则说明最大的子树都比N/2小了,其他肯定也比N/2小,满足作为树的重心的要求。
    		centroid[cnt++] = cur;  //将cur,也就是节点的编号写入centroid数组存起来。
    	return;
    }
    

    典型例题

    https://vjudge.net/problem/POJ-2378

    这个题的题意就是找出某个节点,使得去除这个节点之后,其他节点组成的连通分量的个数都没有大于N/2的,那么其实就是求树的重心。

    个人解答:

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 15000;
    vector<int> G[maxn];
    
    int cnt=0,x,y,N,s[maxn],w[maxn],centroid[maxn] = {0}; 
    
    void dfs(int cur,int fa) {
    	int siz = G[cur].size();
    	s[cur] = 1;
    	w[cur] = 0;
    	for(int i=0;i<siz;i++){
    		int v = G[cur][i];
    		if(v != fa) {
    			dfs(v,cur);
    			s[cur] += s[v];
    			w[cur] = max(w[cur], s[v]);
    		}
    	}
    	w[cur] = max(w[cur], N - s[cur]);
    	if( w[cur] <= N/2 ) 
    		centroid[cnt++] = cur;
    	return;
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	//freopen("in.txt","r",stdin);
    #endif
    	scanf("%d",&N);
    	for(int i=0;i<N-1;i++){
    		scanf("%d%d",&x,&y);
    		G[x].push_back(y);
    		G[y].push_back(x);
    	}
    	dfs(1,-1);
    	sort(centroid,centroid+cnt);
    	if(cnt == 0)cout<<"NONE"<<endl;
    	else 
    		for(int i=0;i<cnt;i++) 
    			cout<<centroid[i]<<endl;
    	return 0;
    }
    

    参考

    1. https://oi-wiki.org/graph/tree-centroid/
    2. https://www.cnblogs.com/knife-rose/p/11258403.html
  • 相关阅读:
    Android开发总结
    LeakCanary原理分析
    机器学习
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 3. 循环
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 1. 神秘朋友
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 0. 准备工作
    远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)
  • 原文地址:https://www.cnblogs.com/kevin-matrix/p/14400344.html
Copyright © 2011-2022 走看看