POJ 1655 Balancing Act

题目大意

找出一棵树的重心。其中,重心指的是一棵树中的一个结点,删去该结点以后形成的各棵子树里最大的那棵子树有最小的结点数目(这篇文章里,我把它称为权)。

闲话

树形DP里有一步是要找树的重心。此题考的恰好是这一点。但这道题目前我的解答不太好,时间上花了250ms。

思路

分别计算删去每个结点后最大子树的结点数目。显然结点之间有重复信息可以利用,因此考虑递归计算。不妨令任意一个顶点为根结点,其余为非根结点。
对于根节点,删去后剩余的最大子树就是根结点的最大子树。
对于非根结点,删去后剩余的最大子树或者是其最大子树,或者整棵树中不包括该结点极其所有子树的部分。因此,我们有

某一结点的权 = max(该结点最大子树的结点数,N - 1- 该结点所有子树结点数之和)

可以留意到,对根结点,这个式子也是成立的。

因此,采用DFS进行后序遍历,每遍历到一个结点,就计算它所带的所有子树结点数和(sum),并利用它子结点的sum值计算它的权值。时间复杂度应当是O(n)的。

其他

也许需要注意N = 1的特殊情况。(与自己的实现细节有关)
鉴于题目输入边时没有指明父结点和子结点,因此在把边加入邻接表时加了两遍;这导致DFS时需要判重,这可能是导致算法变慢的原因之一。

#define M 20010
#include <stdio.h>
int sumOfNodes;
bool Edges[M];
int ansNode = M, ansSum = M;
struct NextEdge
{
    int to;
    int numEdge;
    NextEdge *next;
    NextEdge(int t, int v)
    {
        to = t;
        numEdge = v;
        next = NULL;
    }

};

struct Nodes
{
    int num;
    NextEdge *start;
    NextEdge *tail;
    void addEdge(int to, int numEdge)
    {
        if(start == NULL)
        {
            start = new NextEdge(to, numEdge);
            tail = start;
        }
        else
        {
            tail->next = new NextEdge(to, numEdge);
            tail = tail->next;
        }
        ++num;

    }
    Nodes()
    {
        num = 0;
        start = tail = NULL;
    }


}nodes[M];

int sumOfSubTrees[M];
int maxOfSubTrees[M];

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


void cal(int indexNodes)
{
    NextEdge *tmp = nodes[indexNodes].start;
    sumOfSubTrees[indexNodes] = maxOfSubTrees[indexNodes] = 0;
    while(tmp != NULL)
    {
        if(Edges[tmp->numEdge])
        {
            Edges[tmp->numEdge] = false;
            cal(tmp->to);
            maxOfSubTrees[indexNodes] = max(sumOfSubTrees[tmp->to] + 1, maxOfSubTrees[indexNodes]);
            sumOfSubTrees[indexNodes] += sumOfSubTrees[tmp->to] + 1;
        }
        {
            int t = max(maxOfSubTrees[indexNodes], sumOfNodes - 1 - sumOfSubTrees[indexNodes]);
            if(t < ansSum)
            {
                ansSum = t;
                ansNode = indexNodes;
            }
            else if(t == ansSum && ansNode > indexNodes)
            {
                ansSum = t;
                ansNode = indexNodes;
            }


        }



        tmp = tmp->next;
    }

}


int main()
{
    int cases;
    scanf("%d", &cases);
    for(int i = 0; i < cases; ++i)
    {
        scanf("%d", &sumOfNodes);
        if(sumOfNodes == 1)
        {
            printf("1 0\n");continue;
        }
        ansSum = M;
        ansNode = M;
        int from, to;
        for(int j = 0; j <= sumOfNodes; ++j)
        {
            nodes[j].num = 0;
            nodes[j].start = nodes[j].tail = NULL;
        }

        for(int j = 1; j < sumOfNodes; ++j)
        {
            scanf("%d %d", &from, &to);
            Edges[j] = true;
            nodes[from].addEdge(to, j);
            nodes[to].addEdge(from, j);
        }
        cal(1);
        printf("%d %d\n", ansNode, ansSum);
    }
    return 0;
}

原题链接

POJ 1655 Balancing Act

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值