【求树的重心】POJ - 3107 Godfather

本文介绍了一种寻找树形结构中重心的算法实现方法。通过深度优先搜索(DFS)遍历树的所有节点,并利用动态规划的思想来确定树的重心。树的重心是指找到一个点,该点的所有子树中最大的子树节点数最少。文章提供了详细的代码实现及注释说明。

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

Problem Description

输入n,代表有n个编号1-n的结点。接下来有n行,每行输入u,v分别表示u,v有联系。让你找出Godfather,其实就是找出树的重心,如果有多个满足这样的点,从小到大输出。

思路:第一次做,所以是去网上学习的方法,发现他们都归类为树形dp,然而我dp可能有点差,理解了代码但是没能理解为何是dp。具体的解释看代码里面的注释,,,树的重心(找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int to, next;
};
node e[100000];
//e,head是前向星存图用的数组。n个结点,ans[]用来记录最后结果,vis[]用来标记那些点跑过,Size[]用来记录它的子数的结点数,n-size[]对应的就是父亲那边的结点数。
int head[100000], n, ans[50005], vis[50005], Size[50005], m, top;
void dfs(int u)//dfs遍历所有结点
{
    int v;
    vis[u] = 1;//标记已经走过
    Size[u] = 1;//走到的结点初始化为1
    int t = 0;//用来存储孩子最大的Size
    for(int i = head[u]; ~i; i = e[i].next)
    {
        v = e[i].to;
        if(!vis[v])//没有走过的结点
        {
            dfs(v);
            Size[u] += Size[v];//回潮的时候,更新父亲结点的Size
            t = max(t, Size[v]);//更新t的大小
        }
    }
    t = max(t, n - Size[u]);//求孩子Size,和本身Size最大的
    if(t < m)//更新树的重心所有子数,最大子树的结点,存入结果
    {
        top = 0;
        m = t;
        ans[top++] = u;
    }
    else if(t == m)//多个重心
    {
        ans[top++] = u;
    }
}
int main()
{
    int u, v;
    while(~scanf("%d", &n))
    {
        m = n;
        memset(head, -1, sizeof(head));//初始化
        memset(vis, 0, sizeof(vis));
        memset(Size, 0, sizeof(Size));
        int cnt = 0;
        for(int i = 0; i < n - 1; i++)//前向星存图
        {
            scanf("%d %d", &u, &v);
            e[cnt].to = v;
            e[cnt].next = head[u];
            head[u] = cnt++;
            e[cnt].to = u;
            e[cnt].next = head[v];
            head[v] = cnt++;
        }
        dfs(1);//求树的重心
        sort(ans, ans + top);//排序
        for(int i = 0; i < top; i++)//输出
        {
            if(i) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值