树的基础:树的重心

Dev.c++树的基础:树是一种特殊的图,有一些特殊的性质,重心就是其一。可以用它的定义求解。

声明:感谢oi-wiki所提供的代码。在下***进行了一点点修改。这里先贴出oi-wiki提供的源代码。

声明:感谢大佬zhnzh提供的帮助。一些用词和技术方面是它所提供的。若有出错,请在评论区提出

再次声明:如果有错,不要怪我哟不要脸的

// 这份代码默认节点编号从 1 开始,即 i ∈ [1,n]
int size[MAXN],  // 这个节点的“大小”(所有子树上节点数 + 该节点)
    weight[MAXN],  // 这个节点的“重量”
    centroid[2];   // 用于记录树的重心(存的是节点编号)
void GetCentroid(int cur, int fa) {  // cur 表示当前节点 (current)
  size[cur] = 1;
  weight[cur] = 0;
  for (int i = head[cur]; i != -1; i = e[i].nxt) {
    if (e[i].to != fa) {  // e[i].to 表示这条有向边所通向的节点。
      GetCentroid(e[i].to, cur);
      size[cur] += size[e[i].to];
      weight[cur] = max(weight[cur], size[e[i].to]);
    }
  }
  weight[cur] = max(weight[cur], n - size[cur]);
  if (weight[cur] <= n / 2) {  // 依照树的重心的定义统计
    centroid[centroid[0] != 0] = cur;
  }
}

同时贴出我讲解所需的代码


int size[MAXN],  // 这个节点的“大小”(所有子树上节点数 + 该节点)
    weight[MAXN],  // 记录最大子树 
    centroid[2];   // 用于记录树的重心(存的是节点编号)
void GetCentroid(int u, int fa) {  // u 表示当前节点 (current)
	size[u] = 1;
	weight[u] = 0;
	for (int i = head[u]; i != -1; i = e[i].nxt) {
		if (e[i].to != fa) {  // e[i].to 表示这条有向边所通向的节点。
      		GetCentroid(e[i].to, u);
      		size[u] += size[e[i].to];
      		weight[u] = max(weight[u], size[e[i].to]);
    	}
    }
    weight[u] = max(weight[u], n - size[u]);
	if (weight[u] <= n / 2) {  // 依照树的重心的定义统计
		centroid[centroid[0] != 0] = u;//这是一个小小的优化
  	}
}

因为对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心;
所以这里利用这个计算重心。
这里先将整棵树遍历一遍,遍历过程中,每个节点利用递归将它的子树递归出来(至于怎么弄得等会解释)。
然后算出这棵树上最大的子树,不断的更新它的重心就可以了。
这里要重点解释一下递归求解子树大小。


可以看到他在循环结束后用size记录下节点的大小,当递归到边界时,size为1.
返回上去时,size会加上边界的size。再返回,又会让这个节点的size加上他的孩子的size。
以此类推,他会把整颗子树的size都遍历出来,然后加到这个节点的size中。这就是递归求解节点大小的原理。


到现在为止,树的基础部分都讲完了。

你掌握了吗?

posted @ 2020-10-07 20:25  riced  阅读(475)  评论(4编辑  收藏  举报