题目来源: AcWing 846. 树的重心
一、题目描述
给定一颗树,树中包含 n n n 个结点(编号 1 ∼ n 1∼n 1∼n)和 n − 1 n−1 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中结点数量的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数
n
n
n,表示树的结点数。
接下来 n − 1 n−1 n−1 行,每行包含两个整数 a a a 和 b b b,表示点 a a a 和点 b b b 之间存在一条边。
输出格式
输出一个整数
m
m
m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
二、算法思路
讲树和图的遍历之前,先介绍一下树和图的存储方式。
2.1 树和图的存储方式
树和图有两种特殊的存储方式。而树是特殊的图,是一种无环联通图,因此我们主要介绍图的存储。
图分为有向图和无向图。
- 对于有向图而言,结点 a , b a, b a,b 之间建立一条边只需要建立 a → b a→b a→b 的一条边,或者建立一条 b → a b→a b→a 的一条边。
- 对于无向图而言,结点 a , b a, b a,b 之间既要建立一条 a → b a→b a→b 的一条边,又要建立一条 b → a b→a b→a 的一条边。
因此,我们也可以把无向图看成一种特殊的有向图。
有向图的存储方式:
- 邻接矩阵
g
[
a
,
b
]
g[a, b]
g[a,b],表示有
a
→
b
a→b
a→b 的一条边,用的比较少。
- 空间复杂度 O ( n 2 ) O(n^2) O(n2);
- 无法记录重边。
- 邻接表,也就是在图中每一个结点
i
i
i 上开一个单链表,链表上记录的
i
i
i 结点可以走到的结点,插入时使用头插法,用的非常多。
邻接表存储方式的代码表示
#include <iostream>
using namespace std;
const int N = 1e5 + 10, M = 2 * N; // N既代表了结点数,也代表了顶点之间的关系,因为是无向图,边数要乘2
int h[N], e[M], ne[M], idx;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 初始化
void init() { memset(h, -1, sizeof h); }
2.2 树和图的遍历
树和图的遍历方式分为两种:深度优先遍历(DFS)和宽度优先遍历(BFS)。
原理大家有数据结构基础的应该都清楚,下面直接上代码模板,宽度优先搜索的模板以后讲。
深度优先遍历代码模板
void dfs(int u)
{
st[u] = true; // 标记一下,已经搜过了
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j]) dfs(j);
}
}
2.3 本题思路
在本题中,使用深度优先遍历,在遍历时返回当前子树包含的结点数量。
因为搜索的过程中,已经被
s
t
st
st 标记的父结点区域都可以通过减法获得,每次递归都是计算没被遍历的子树,因此只用遍历一次即可。
三、代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = 2 * N;
int h[N], e[M], ne[M], idx;
bool st[N];
int n;
// 全局的答案
int ans = N;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 返回以u为根的子树中点的数量
int dfs(int u)
{
st[u] = true;
// sum计算的是当前子树结点总数
// res记录删除该结点后,其他连通块最大值
int sum = 1, res = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!st[j])
{
int s = dfs(j);
res = max(res, s);
sum += s;
}
}
res = max(res, n - sum);
ans = min(ans, res);
return sum;
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1);
printf("%d\n", ans);
return 0;
}