树的重心:
该图中,把某个点作为删除,生成的所有连通图中找到最多点的连通图,存他的点的个数,再把该点复原,把下一个点重复操作,直到找完所有点。再从所有存的最多点的连通图的点的个数中找到点最少的点的个数。该删除的点就是重心。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10; //数据范围是10的5次方
const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边
int h[N]; //邻接表存的是编号为i的节点的邻接表,存的都是该节点的直接连着的子节点
int e[M]; //存储元素
int ne[M]; //存储列表的next值
int idx; //点的索引,从0开始
int n; //题目所给的输入,n个节点
int ans = N; //表示重心的所有的子树中,最大的子树的结点数目
bool st[N]; //记录节点是否被访问过,访问过则标记为true
void add(int a, int b)
{//头插法
//如图 如1与2之间要有一条线,让2的ne为1,再让h[1]为2的索引。
//这样h[1]就是1节点存的最后一个相连的点,如图就是7节点。
//而在索引表内部,通过头插法的方式(即每次ne指向上一个点(h存的就是上一个点)),索引表为:7->4->2
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
//返回以u为根的子树中节点的个数,包括u节点
int dfs(int u)
{
int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数
st[u] = true; //标记访问过u节点
int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点
//访问u的每个子节点,即u的邻接表。因为初始化为-1,所以最后一个点的ne指向一定是默认的-1
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
//因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过
if (!st[j]) {
int s = dfs(j); // u节点的单棵子树节点数 如图中的size值
res = max(res, s); // 记录最大联通子图的节点数
sum += s; //以j为根的树 的节点数
}
}
//n-sum 如图中的n-size值,不包括根节点4;
res = max(res, n - sum); // 选择u节点为重心,最大的 连通子图节点数
ans = min(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数
return sum;
}
int main()
{
memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点
scanf("%d",&n); //表示树的结点数
// 题目接下来会输入,n-1行数据,
// 树中是不存在环的,对于有n个节点的树,必定是n-1条边
for (int i = 0; i < n - 1; i++) {
int a, b;
scanf("%d%d",&a,&b);
add(a, b), add(b, a); //无向图,让a和b连线
}
dfs(1); //可以任意选定一个节点开始 u<=n
printf("%d\n",ans);
return 0;
}