Acwing846树的重心---------dfs(邻接表)

地址:

https://www.acwing.com/problem/content/description/848/

描述:

思路:

本题的本质是树的dfs, 每次dfs可以确定以u为重心的最大连通块的节点数,并且更新一下ans。

也就是说,dfs并不直接返回答案,而是在每次更新中迭代一次答案。

这样的套路会经常用到,在 树的dfs 题目中

树的dfs.png

 总结以u为根的子树个数可由dfs(j)不断递归得到

无标题.png

 特别注意:

1.png


 由于本题给出的图是无向图,假如首次遍历,不论取那个节点,它都会把与他联通的所有子树全部加入sum,最终sum都==n。

例如从4开始遍历的话,上面一块和下面两块连通块数目都被计算过并被加到sum中。

循环结束后sum==n,没必要取res=max(res,n-sum)。

res=max(n-sum,res)对于首次遍历的节点来说是确实没必要的;但对后续的子节点dfs时由于其父节点已经遍历过了,for循环中就不能获取到以父节点为根的子树。

比如节点4,sum就只能取到节点4下面的两个子树节点数之和,取不到它的父节点,这种情况就需要max(n-sum,res),n-sum指的是去除下面的两个子树节点数,该树还剩下的节点数。

 

代码:

#include <cstring>
#include <iostream>
#include <climits>
using namespace std;
//首先写出dfs的模板
const int N=1e5+10;
//因为是无向图,所以每个节点至多对应2n-2条边
const int M=N*2;
//节点个数
int n;
int h[N],e[M],ne[M],idx;
bool stu[N];
//ans存储的是将重心删除后剩余各个连通块中点数的最大值
//根据重心的定义,将中心删除后,剩余各个连通块中点数的最大值最小,所以ans初始值要尽可能取大
int ans=INT_MAX;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//返回的是以u为根节点的子树大小
int dfs(int u){
    stu[u]=true;
    //sum记录当前以u为根的子树大小,res记录删除重心后各个联通块的最大值
    int sum=1,res=0;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!stu[j]){
            int s=dfs(j);//以j为根的子树大小
            sum+=s;//将该子树添加到sum中,来计算出以u为根的子树完整大小
            //以j为根的子树是以u为根的删除u后的联通块,所以要与res进行比较取出最大值
            res=max(res,s);
        }//if
    }//for
    //n-sum表示的是减掉u为根的子树,整个树剩下的点的数量
    res=max(res,n-sum);
    ans=min(res,ans);
    return sum;
}
int main(){
    //首先初始化使节点都指向空
    memset(h,-1,sizeof h);
    cin>>n;//由题得,边数=n-1;
    for(int i=0;i<n-1;i++){
        int a,b;
        cin>>a>>b;
        //因为是无向图,所以两个方向都要加
        add(a,b),add(b,a);
    }
    dfs(1);//节点可任意选
    cout<<ans;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值