树的重心

点击查看代码
#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;
}
posted @ 2025-11-23 02:34  AnoSky  阅读(6)  评论(0)    收藏  举报