树的重心(深度优先遍历)

先看题目:

给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式

第一行包含整数 n,表示树的结点数。

接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。

输出格式

输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。

题目比较阴间,我们先翻译一下

 解答代码:

#include<iostream>
#include<cstring>

using namespace std;

const int N = 100010;
const int M = 2*N;

int n;
int h[N],e[M],ne[M],idx;
bool st[N];//用来判断编号为 i 的结点是否被搜过
int ans = N;//这个ans要足够大,不然后面的min(ans,res)无法更新

void add(int a,int b)//这里的a和b指的是结点的值,本题环境中可以理解为是结点的编号
                     //在值为 a 结点的后面,插入结点 b 
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx++;
}

int dfs(int u)//这里的dfs()返回的是编号u结点的子树的所有节点数
              //注意:一定是子树,而不包括父节点
{
    int res = 0;//res存储的是编号u结点的所有子树中节点数的最大值
            //换人话说,就是编号u结点所有子树中,结点数最多的那个子树的节点数
    int sum =1;//指的是包含编号u结点,编号u结点的子树的所有的节点数和
    st[u] = true;
    
    for (int i = h[u];i!= -1;i =ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
        int s  =dfs(j);//这里的dfs(j)代表编号j结点的所有子树的所有节点数
        sum += s;//sum表达的是编号u结点的所有子树的所有节点数
                 //而编号j结点是编号u结点的子节点,或者可以理解为是编号u结点的一棵子树
                 //的开头。
                 //那么就可以得到下列公式:
                 //dfs(u) = dfs(j) + dfs(k) +...+ dfs(l) +1
                 //公式中的j,k,...,l 就是编号u结点的所有子结点(一定是子结点,而不是父节点)
                 //因为编号u一定是从父节点走过来的,那么父节点一定已经被标记了被搜索过
                 //所以不可能再往回走
                 //而公式中的1就是编号u结点本身。
                 //可以类比斐波那契额数列的公式:f(n) = f(n-1)+f(n-2),是差不多的道理
        res = max(res,s);//得到编号u结点所有子树中结点数最多的子树的结点数
        }
        
    }
    res = max(res,n-sum);//这个代码可以放在图中讲
    ans = min(ans,res);//这一句代码是为了题目服务,找到最小的最大值
        
    return sum;//最后的返回值是sum,而不是ans
    
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    cin >> n;
    
    memset(h,-1,sizeof(h));
    
    for (int i = 0;i<n-1;++i)
    {
        int a,b;
        cin >> a >> b;
        
        add(a,b);
        add(b,a);
    }
    
    dfs(1);//这里随便取一个小于n的数就行
           //之所以随便取一个数就行,是因为最后要的是将重心删除后,
           //剩余各个连通块中点数的最大值的最小值。
           //而不是特定结点的所有子树的结点数之和
           //而无论这个参数取多少,dfs一定能走遍所有的结点,比较所有的删除特定点
           //后的剩余各个连通块中点数的最大值
           
    cout << ans ;
    return 0;
}

 1.int dfs(int u){}函数

具体的 深度优先遍历 过程在 树与图()那一篇blog中有详解,不懂的可以去看

这一篇我们主要看一看如何运用在实际题目中运用 深度优先遍历 

(1)

首先 dfs(int u) 的含义在本题的环境中是返回编号u结点的所有子树的所有结点数之和(注意:一定是子树

(2)

int res = 0;//res代表的是编号u结点的所有子树中结点数最多的子树的结点数

int sum = 1;//sum代表的是编号u结点的所有子树的所有结点数之和
            //这里之所以初始值是1,因为将编号u结点也包含进去了

(3)

根据题意,我们可以推断出一个递归式:

dfs(u) = dfs(j) + dfs(k) +...+ dfs(l) +1

其中: j,k,l... 是编号u结点的所有子节点,1代表编号u结点本身也算结点之一。

(4) 

res=  max(res,s);

这段代码将编号 u 结点的所有子树中拥有结点数最多的子树的结点数记录下来。

int s = dfs(j);

s就是所要进入的子树的其中所有的结点数。

(5)

res = max(res,n-sum);

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值