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

地址:

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

描述:

Acwing846树的重心---------dfs(邻接表)_第1张图片

思路:

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

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

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

Acwing846树的重心---------dfs(邻接表)_第2张图片

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

Acwing846树的重心---------dfs(邻接表)_第3张图片

 特别注意:

Acwing846树的重心---------dfs(邻接表)_第4张图片


 由于本题给出的图是无向图,假如首次遍历,不论取那个节点,它都会把与他联通的所有子树全部加入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指的是去除下面的两个子树节点数,该树还剩下的节点数。

Acwing846树的重心---------dfs(邻接表)_第5张图片

 

代码:

#include 
#include 
#include 
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>a>>b;
        //因为是无向图,所以两个方向都要加
        add(a,b),add(b,a);
    }
    dfs(1);//节点可任意选
    cout<

你可能感兴趣的:(Acwing刷题,考研数据结构整理,数据结构,算法)