树上的动态规划学习4 - 树的重心(质心)

本文探讨了一道树上的动态规划题目,旨在寻找一颗无根树中的某个节点作为根节点时,使得最大子树的节点数达到最小值。文章提供了一个高效的算法实现,并通过示例输入输出解释了算法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树上的动态规划经典题目。题目的原意是对于一棵n个节点的无根树,找到一个点,使得把树变成以该节点为根的有根树时,最大子树的节点数最小。也就是说,删除这个点后最大连通块的结点数最大

sample input:
7
2 6
1 2
1 4
4 5
3 7
3 1

sample output
1 2

注意这里所说的最大子树的节点数是包括从根到叶子的全部节点。

这题我觉得并不简单。最直接的方法,我们可以对每个节点都进行一次无根树变有根树,然后记录其最大子树的节点数。最后找出是哪个节点的最大子树的节点数最小。但是这样的话复杂度为O(n^2),肯定慢了。

另一个方法是看等价条件"删除这个点后最大连通块的结点数最大",随便找一个节点做根,然后将该树变成以该节点为根的有根数,DFS遍历每个节点i,记录节点i的最大子树的节点树max{d(j)}和i的上方子树(也就是除了i及其各子树外的节点所构成的树)的节点数(应为n-d[i])的最大值。为什么这个方法可行呢?因为如果以节点i为根的话,它的子树也就是各个j所对应的子树加上上方的子树。看看紫书的图9-13就可以明白(P281)。

#include <iostream>
#include <vector>
using namespace std;
#define N 1000000+10
vector<int> G[N];   //stores the graph
int n;
int parent[N]={0};
int d[N]={0};
int g_maxSubTree=0xFFFF;
int g_center=0xFFFF;

void read_tree()
{
    int u, v;
    cin>>n;
    for (int i = 0; i < n - 1; i++)    //注意无向图n个节点,边数为n-1
    {
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
}

void dfs(int u, int father)
{
    int maxSubTree=0;
    if (G[u].size()==0) {
        d[u]=1;
        return;
    }

    for (int i = 0; i < G[u].size(); i++)
    {
        if (G[u][i] != father) { //加此判断,防止无限递归
            parent[G[u][i]] = u;
            dfs(G[u][i], u);
            maxSubTree=max(maxSubTree, d[G[u][i]]);
            d[u]+=d[G[u][i]];
        }
    }
    d[u]+=1;  //include itself
    maxSubTree = max(maxSubTree, n-d[u]);
    if (maxSubTree<g_maxSubTree) {
        g_maxSubTree = maxSubTree;
        g_center = u;
    }
}

int main()
{
    read_tree();
    int root;
    cin >> root;
    parent[root] = -1;
    dfs(root, -1);
    for (int i = 1; i <= n; i++)
        cout << parent[i] << ' ';
    cout<<endl;
    cout<<"center node is "<<g_center<<endl;
    cout<<"min_max subtree # of nodes is "<<g_maxSubTree<<endl;
    return 0;
}


<think>好的,用户想要用Python计算树的重心。首先,我需要明确树的重心的定义。树的重心是指在树中删除某个节点后,剩下的各个子树中节点数的最大值最小,这样的节点即为重心。每个树至少有一个重心,最多两个。 接下来,我需要考虑如何用算法实现这个计算。通常,树的重心可以通过深度优先搜索(DFS)来遍历每个节点,计算其子树的节点数,并找出满足条件的节点。具体步骤可能包括: 1. 构建树的邻接表表示。用户可能提供的是节点间的连接关系,比如边的列表。需要将这些转化为邻接表,方便遍历。 2. 确定一个根节点,比如默认0,或者由用户指定。 3. 使用DFS遍历每个节点,计算每个节点的子树大小,同时记录最大子树的大小。 4. 在遍历过程中,维护当前找到的最小最大值,并更新重心的候选节点。 5. 处理可能的多个重心情况,比如两个重心的情况。 然后,我需要参考用户提供的引用内容。例如,引用[1]提到计算质心是通过顶点坐标的平均值,但这里树的重心是图论中的概念,不是几何上的质心,所以可能需要不同的方法。引用[2]提到了区域属性的计算,可能涉及遍历结构,比如使用findContours,但可能不直接适用。引用[3]中的Python代码示例使用reduce等函数,但可能和DFS无关。 因此,正确的做法是编写一个基于DFS的算法,遍历树结构,计算每个节点的子树大小,并确定重心。需要注意避免重复访问父节点,所以在邻接表中需要记录父节点。 现在,我需要写出具体的代码步骤: - 构建邻接表:例如输入是边的列表,转化为每个节点的邻居列表。 - 初始化变量:总节点数n,记录每个节点的子树大小,全局的最小最大子树大小,以及结果列表。 - DFS函数:递归遍历每个子节点,累加子树大小,并更新当前节点的最大子树大小。比较删除该节点后的最大子树是否更小,如果是,则更新重心。 - 处理根节点的父节点问题,避免循环访问。 可能的代码示例需要处理这些步骤。另外,测试用例也很重要,比如给出一个简单的树结构,验证代码是否能正确找到重心。 最后,用户可能需要相关的应用场景,比如网络中的关键节点识别,或者优化树的平衡结构。同时,可能的问题包括如何处理动态变化的树,或者如何找到所有重心等。</think>### 如何用Python计算树的重心 树的重心是图论中的重要概念,指删除该节点后形成的最大子树的节点数最小。以下为Python实现方法及示例代码: --- #### 核心算法步骤 1. **构建邻接表**:将树表示为邻接表结构,例如使用字典或列表存储每个节点的邻居。 2. **DFS遍历计算子树大小**:通过深度优先搜索递归计算每个节点的子树节点数,并记录删除该节点后的最大子树大小。 3. **比较并更新重心**:维护全局最小值,找到使最大子树最小的节点。 --- #### 代码示例 ```python from collections import defaultdict def find_centroid(n, edges): adj = defaultdict(list) for u, v in edges: adj[u].append(v) adj[v].append(u) min_max_subtree = float('inf') centroids = [] visited = [False] * n def dfs(node): nonlocal min_max_subtree visited[node] = True size = 1 max_sub = 0 for neighbor in adj[node]: if not visited[neighbor]: child_size = dfs(neighbor) size += child_size max_sub = max(max_sub, child_size) # 比较删除该节点后的剩余部分(可能包含父节点方向的子树) remaining = n - size max_sub = max(max_sub, remaining) # 更新重心 if max_sub < min_max_subtree: min_max_subtree = max_sub centroids.clear() centroids.append(node) elif max_sub == min_max_subtree: centroids.append(node) return size dfs(0) # 假设根节点为0 return centroids # 示例输入:4个节点的树,边为[(0,1), (1,2), (2,3)] edges = [(0,1), (1,2), (2,3)] centroids = find_centroid(4, edges) print("树的重心节点:", centroids) # 输出: [1, 2] ``` --- #### 关键说明 1. **邻接表构建**:使用字典存储每个节点的连接关系,支持快速遍历。 2. **递归逻辑**:`dfs`函数返回当前子树的节点数,并动态计算最大子树大小。 3. **重心判断**:比较删除当前节点后的最大子树是否全局最小,若相等则记录多个重心。 --- #### 应用场景 1. 网络拓扑中的关键节点检测 2. 树的平衡分割(如分布式系统数据分片) 3. 动态规划优化(如树形DP问题) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值