解题思路
-
题目给出树的边都是无向边,所以该树可以被看成是图。
-
为什么输出最大值的最小值?
- 因为可能不止一个重心
-
存储方式:
- a. 邻接矩阵(适合存储稠密图)
- b. 邻接表(拉链法(本题存法))
-
全局变量ans的作用(记录最小的最大值)
-
一般来说题目只需要遍历一次,需要有一个
bool数组st[N]
记录哪些点已经被遍历过了。 -
使用头插法构建邻接表(e[idx] = b; ne[idx] = h[a]; h[a] = idx++;)
-
// 树&图的深度优先搜索代码 void dfs(int u) { st[u] = true; // 标记一下,已经被搜过了 // 遍历节点u的每一个出边 for(int i = h[u]; i != -1; i = ne[i]) { // 存储该节点对应图中的位置 int j = e[i]; // 如果该节点尚未被dfs访问过,则进行递归 if(!st[j]) dfs(j); } }
-
根据这道题的要求,dfs()需要做的对应的操作
- dfs()函数返回以u为根的节点个数 sum += s;
- 去除重心后连通块的最大值,max(n - sum, res)
- 全局变量ans---最大值的最小值,min(ans, sum)
// 以u为根的子树中点的数量
int dfs(int u)
{
st[u] = true; // 标记一下,已经被搜过了
// "sum":以u为根的子树的节点总个数,"res":将u删掉后每一个连通块大小的最大值
int sum = 1; res = 0;
for(int i = h[u]; i != -1; i = ne[i])
{
// 存储当前节点对应图的位置
int j = e[i];
// 如果该节点尚未被dfs访问过,则进行递归
if (!st[j])
{
int s = dfs(j); // return sum 返回的值
res = max(res, s);
sum += s;
}
}
res = max(res, n - sum);
ans = min(ans, res);
return sum;
}
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// 无向图两节点之间有两条边
const int M = N * 2;
int h[N], e[M], ne[M], idx, n;
// 全局变量,用于记录去掉重心后的子树节点个数所有最大值的最小值
int ans = N;
// 记录该节点是否在dfs的过程中被遍历过
bool st[N];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dfs(int u)
{
st[u] = 1;
// 以u为根的子树的节点个数, res:除去u的连接块的节点个数的最大值
int sum = 1, res = 0;
for(int i = h[u]; i!= -1; 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(res, ans);
return sum;
}
int main()
{
memset(h, -1, sizeof h);
cin >> n;
int a, b;
for(int i = 0; i < n - 1; i ++)
{
cin >> a >> b;
add(a, b), add(b, a);
}
dfs(1);
cout << ans << endl;
return 0;
}