点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=N*2;
//结合dfs的特点
int n;
int h[N],e[M],ne[M],idx;
int ans=N;
bool st[N];
//理解细节但不执着于细节,将复杂的代码抽象为具体的功能
//分成上下文即可,上文是递归分解,直到最后一个开始回溯,下文是运算,最后一层的返回值是关键
//从微观到宏观
//h[a]存储的不是对应的头节点,而是a本身就代表头节点,如果无子树的话那么h【a】就是-1,如果有就是第一个子节点
//向邻接表中的表头中增加节点
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u)
{
//--------------初始化----------------
st[u]=true;
//size删除u后,剩余连通块中最大节点数
//sum,u的所有子树节点数总和
int size=0,sum=0;
//深层搜索一定有搜索的终点,而本题的终点就是最后一个子节点
//也就是这个for循环不执行的时候,就是u的所有邻居都被访问过,都是父节点,或者u只有一个邻居,就是父节点
//这是递归基,但不是真正的逻辑结束点,还要经过循环外两步的计算
//---------------遍历子树-----------------
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
//因为是双向,排除遇到父节点的情况
if(st[j]) continue;
//s是以j为根的子树的总节点数(包括自己)
//s相当于是size不可或缺的一部分了,每个s都有可能是size所以依次判断一下
int s=dfs(j);
size=max(size,s);
//节点u连接的联通体的节点总和(不包括自己),size是单个最大,s是对任意一个连通体
sum+=s;
}
//----------------计算父节点连通块----------------
//n-sum-1是相对的,没有说谁必须是子节点谁是父节点,这取决于深度搜索的顺序,第一个就全是子节点
//看是u连接的节点数最大还是连接u的另外部分大
//这样设置也是因为树的遍历不可以重复的原因
//到这是真正的结束,计算出来切割的真结果了
size=max(size,n-sum-1);
//在所有节点中取最小的
//------------------更新答案--------------------
ans=min(ans,size);
//返回以u为根的子树总节点数(包括u自己)
return sum+1;
//------------##让dfs的思想深入人心##-------------------------
}
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",ans);
return 0;
}