试着写一下这道题的题解,所有的解释都在注释里了,有啥问题再往里面加
C++ 代码
// 如果没看懂题目,请看y总vcr(1:34:20 -- )https://www.acwing.com/video/21/
// 树是一种特殊的图,因此只需要学会图的深度优先遍历即可
// 这道题的数据结构是邻接表,为每个点都创建一个单链表 树和图如何存储请看vcr:(1:15:40 --)https://www.acwing.com/video/21/
// 这道题用树和图的深度优先搜索/遍历做,把所有的点都删除一遍,
//并且用res保存删除当前点以后的联通块中的最大连通块的节点数量
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010, M = N * 2;
int n;
int h[N]; // N个链表的链表头,也是下坐标的数组
int e[M]; // e存的是每个节点的值
int ne[M]; // ne存的是每个节点的next指针
int idx;
bool st[N]; // 开个bool数组判断哪些点已经被遍历过了
int ans = N; // 全局答案存的是最小的最大值
void add(int k, int x) // 插入一条k指向x的边
{
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++ ;
}
// 返回全局答案
// 深度优先遍历可以快速求出当前要删除点的两个叶节点所在连通块的大小
int dfs(int u)
{
// 注意:删除u点以后,连通块被分成三部分:u点之下的两个叶节点分别构成一个连通块,
//与u之上的连通块,一共三个连通块,而dfs的效果就是求出这三个连通块当中最大的。
st[u] = true; // 标记一下当前这个点已经被搜过了
// sum表示以u为根的连通块节点数量,当前的u点算一个点,所以sum从1开始
// res表示删除u点后,两个叶节点中,最大连通块的大小
int sum = 1, res = 0;
// 遍历一下u的所有初边,跟遍历单链表是一样的
for (int i = h[u]; i != -1; i = ne[i])
{
// j存的是当前链表的节点对应的图里面点的编号, 因此u -> j表示在图中从u指向j的边
int j = e[i];
if (!st[j]) // 如果没有搜到j的话就一条道走到黑一直搜
{
int s = dfs(j); // s表示当前的以儿子j为根节点的子树(连通块)的大小
res = max(res, s);
// 即以儿子为根节点的子树是以u为根节点的子树的一部分,因此需要将其大小累加到sum中。
sum += s;
}
}
res = max(res, n - sum); // n - sum表示u点之上的连通块的大小
//上面的代码都执行完毕以后,res里面存的就是删除u点后最大连通块的大小
ans = min(res, ans);
return sum; // sum在递归dfs的时候要用到,因此不能去掉return sum
}
int main()
{
cin >> n;
// 初始化头结点链表h
memset(h, -1, sizeof h);
for (int i = 0; i < n; i ++ )
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs(1); // 从第一个点开始深度优先遍历
cout << ans << endl;
return 0;
}