[图dfs] aw846. 树的重心(dfs+图+好题+模板题+思维)

文章目录

1. dfs + 图

846. 树的重心

这篇题解相当不错!!

在这里插入图片描述

思路:

  • 邻接表存树,注意无向边,正反存两次就行了。树的节点数是 n但是由于需要存正反两次边,作为无向图,故针对 值 e[N*2]、下一个 ne[N*2],都需要开两倍空间进行存储。
  • 考虑连通块种类
    • 重心删除后的各个子树构成的连通块。
    • 重心父节点所在的连通块。
  • 我们可以通过 dfs 得到以任意节点作为根节点,它子树的节点数量。可以让 dfs 就返回以当前重心作为树的节点数量,记为 size(u),那么,dfs 下去再回溯上来就是重心的各个子节点作为根节点的点数量,那么在这些中取 max 就是重心删除后各个子树构成连通块的最大值了,再拿这个最大值与 n-size(u) 取个最大就是结果了当前 u 节点所有连通块的最大结果了,当然最后再和每个节点的 max 结果取个 min 就行了。

只搜一个根,将所有的情况全部搜到,当然在此是无根树,搜任意的根均可。

代码中会给出详细注释,这也算是我入门图论的第一道实战题目!很综合的一道题目!

本题采用全局变量的方式来记录 dfs 的中间变量,即采用了 ans 这个值,注意,需要将其赋成最大值…一开始忘记了,结果取 min 后为 0。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e5+5;

int n;
int h[N], e[N * 2], ne[N * 2], idx; // 邻接表
bool st[N];     // 判断节点是否已经被访问过了

int ans = N;    // 最终结果,需要和每个节点返回的max取min

void add(int a, int b) {    // 在邻接表中加上一条a->b的单向边
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

// 暴力枚举,深搜,找到树中每个节点的重心连通块最大值,每次取个min即可
// 返回以u为根的子树中点的数量
int dfs(int u) {
    st[u] = true;   // u已经被访问过了,即它被当过重心算过了
    
    // sum表示当前子树的大小包括重心,所以初始化为1
    // res表示删除当前节点后连通块的最大值
    int sum = 1, res = 0;   
    for (int i = h[u]; i != -1; i = ne[i]) {    // 遍历它的出边所有节点,选出最大的连通块res
        int j = e[i];       // 链表节点对应到图中节点的编号,是u的所有出边
        if (!st[j]) {       // 出边未被遍历过,则说明它和它的子树都未被遍历过,在此保证只遍历一边
            int s = dfs(j); // 递归得到节点j为根的子树各个连通块节点的数量
            res = max(res, s);  // 取max得到这条链上所有连通块的最大值
            sum += s;           // 加上s是对所有连通块求和,得到这个子树的所有节点数
        }
    }
    
    res = max(res, n - sum);    // 最大子树的连通块节点数res和重心父节点所在的连通块取max
    
    ans = min(ans, res);        // 至此,根节点的重心最大连通块数量已经求出,即res,每次res取min即可
    
    return sum;
}

int main() {
    memset(h, -1 ,sizeof h);
    
    cin >> n;
    for (int i = 0; i < n - 1; ++i) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);   // 无向边
    }

    dfs(1);

    cout << ans << endl;
    
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值