• 树的重心


    树的重心

    定义

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

    性质

    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
  • 相关阅读:
    【字符串哈希】The 16th UESTC Programming Contest Preliminary F
    【推导】The 16th UESTC Programming Contest Preliminary L
    【推导】zoj3846 GCD Reduce
    【spfa】【动态规划】zoj3847 Collect Chars
    【搜索】【组合数学】zoj3841 Card
    【贪心】【字典树】Gym
    【贪心】【后缀自动机】Gym
    【拉格朗日插值法】【找规律】【高精度】Gym
    【二分】【动态规划】Gym
    【软件开发综合实验】文本压缩软件
  • 原文地址:https://www.cnblogs.com/kevin-matrix/p/14400344.html
Copyright © 2020-2023  润新知