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

你可能感兴趣的:(dp,poj)