51、【图】树的重心——邻接表+DFS(C/C++版)

题目描述

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

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

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

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

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

输出格式

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

数据范围

1≤n≤105

输入样例

9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

输出样例:

4

前置知识

42、【链表】静态单链表(C/C++版)

树的重心解释

题目分析

在树中删除一个点后,将会构成一个森林,在森林中将会拥有一个结点最多的树,即最大连通块。求树的重心相当于就是在树中删除点的情况下会出现不同的森林,找到一种森林,该森林中最多结点的树比其余情况森林中最多结点的树的结点数最小,即最小的最大连通块。

首先,从某一结点u出发开始寻找,以结点u进行划分,将会得到以u为根的子树(下半部分)和上半部分。再来求出以u根的树中各个子树的结点个数,并取其中结点个数最多的子树,记其中的结点个数为res。

然后,再对上半部分。设图中结点个数为size,在第一步求各子树结点个数时,将各子树的结点个数累计求和并加上根节点,可得到以u为根的树中结点数量为sum,则可求得结点u的上半部分结点数为size - sum。

然后将res和上半部分进行比较找出具有结点个数最多的那棵树,即max(res, size - sum),便可得到删除u后这种情况下,所得到的最大连通子图。
在这里插入图片描述
以此方式,依次探索图中各个节点,将会得到各个最大连通子图。从中取结点个数最小的最大连通子图,即为所求。

树相当于是一种无环连通图,因此可用图的方式对其进行处理。

算法实现

#include <stdio.h>

const int N = 1e5 + 10, M = N * 2;
int n, ans = N;                 // ans记录剩余各连通块中数的最大值
int h[N], e[M], ne[M], idx;     // 邻接表
bool visit[N];                  // 记录i是否被搜过

int max(int a, int b)   {   return a > b ? a : b;   }
int min(int a, int b)   {   return a < b ? a : b;   }

void add(int a, int b){      // 头插法,邻接表存边
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfs(int u){
    int res = 0, sum = 1;       // res记录u的最大子树的节点数,sum记录以u为根的树中的所有的节点数(从下开始去除上一半,n-sum表示u上面部分的节点数,初始化为1表示当前只有结点自身)
    visit[u] = true;            // 标记u这个点被搜过

    for(int i = h[u]; i != -1; i = ne[i]){      // i是边的编号
        int j = e[i];               // j是u的邻接点
        if(!visit[j]){              // 如果没有被搜过
            int s = dfs(j);         // s是以j为根的子树的结点数
            res = max(res, s);      // 记录u的最大子树的结点
            sum += s;               // 累加u的各子树的结点数
        }
    }
    // 由于深度遍历后续结点后,之前的结点已被标记为访问过,因此需要用n-sum来获取以上一个节点为根的子树结点个数。 max(res, n - sum)
    // 更新答案,取各连通块中最大值进行比较得出的最小值 
    ans = min(ans, max(res, n - sum));   
    return sum;
}



int main(){
    scanf("%d", &n);
    for(int i = 0; i <= n; i++)        h[i] = -1;	// 初始化为头结点都指向-1(空指针)
    for(int i = 0; i < n - 1; i++){    	// 树中是不存在环的,对于有n个节点的树,必定是n-1条边
        int a, b;       scanf("%d%d", &a, &b);
        add(a, b);      add(b, a);      // 无向图由a可到b,由b可到a
    }
    dfs(1);             // 任取一点开始搜索
    printf("%d", ans);

    return 0;
}

参考资料:
9.101 树的重心 树形DP——信息学竞赛培训课程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值