poj 1655 Balancing Act(树的重心+dfs)

poj 1655 Balancing Act(树的重心+dfs)

Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1…N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the largest tree in the forest T created by deleting that node from T.
For example, consider the tree:

在这里插入图片描述
Deleting node 4 yields two trees whose member nodes are {5} and {1,2,3,6,7}. The larger of these two trees has five nodes, thus the balance of node 4 is five. Deleting node 1 yields a forest of three trees of equal size: {2,6}, {3,7}, and {4,5}. Each of these trees has two nodes, so the balance of node 1 is two.

For each input tree, calculate the node that has the minimum balance. If multiple nodes have equal balance, output the one with the lowest number.
Input
The first line of input contains a single integer t (1 <= t <= 20), the number of test cases. The first line of each test case contains an integer N (1 <= N <= 20,000), the number of congruence. The next N-1 lines each contains two space-separated node numbers that are the endpoints of an edge in the tree. No edge will be listed twice, and all edges will be listed.
Output
For each test case, print a line containing two integers, the number of the node with minimum balance and the balance of that node.
Sample Input
1
7
2 6
1 2
1 4
4 5
3 7
3 1
Sample Output
1 2

  • 题目大意
    给你一棵树,让你求它的重心
  • 树的重心
    定义:
    找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
    性质:
    a.树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
    b.把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
    c.一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
    d.一棵树最多有两个重心,且相邻。
    算法:
    1.对一个结点,我们考虑它有 x个子节点 然后它还有父节点,我们把上父节点所在的子树称为上方子树,所以说这个结点一共有 x+1个子树,我们要找的是这 n+1个子树中结点数目最多的子树。
    2 .怎么样找子树的结点树呢?dfs(结点)往下搜,然后遍历这个结点所有的子节点,找出其中的最大的一棵子树。(当然我们不能往父节点搜呀,不能往上找呀)
for(int i=head[x];i!=-1;i=side[i].next)//遍历每一i个儿子结点 
    {
        if(side[i].to==fa) 
			continue;
        else
        {
            t=dfs(side[i].to,x);
            bx=max(bx,t);//用子树的节点数更新bx
            sum+=t;
        }
    }

3 .那最后剩下的上方子树的大小怎么求呢???我们用补集的思想,一个数的结点个数一共n 去掉那 x个子树的结点数,再去掉这个结点本身,剩下的结点数便是上方子树的结点数啦。我们用sum记录了已经用过的结点数,所以n-sum 就是上方结点的数目了。

  • 完整代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=20030;
struct node{
	int to;
	int next;
}side[2*maxn];
int head[maxn];
int n;
int maxx=0x3f3f3f3f;///重心的最大子树结点数
int id=0x3f3f3f3f;///树的重心
int cnt=0;
void init()
{
	memset(head,-1,sizeof(head));
	cnt=0;
	maxx=0x3f3f3f3f;///重心的最大子树结点数
	id=0x3f3f3f3f;///树的重心
}
void add(int x,int y)
{
	side[cnt].to=y;
	side[cnt].next=head[x];
	head[x]=cnt++;
}

int dfs(int x,int fa)
{
    int sum=1;///x结点所有儿子的结点的个数之和 
	int bx=0;///儿子中最大的一个节点数 
	int t;
    for(int i=head[x];i!=-1;i=side[i].next)//遍历每一i个儿子结点 
    {
        if(side[i].to==fa) 
			continue;
        else
        {
            t=dfs(side[i].to,x);
            bx=max(bx,t);//用子树的节点数更新bx
            sum+=t;
        }
    }
    bx=max(n-sum,bx);//这一步不能少
    if((bx<maxx)||(bx==maxx&&id>x))
    {
        id=x;
        maxx=bx;
    }
    return sum;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		init();
		for(int i=0;i<n-1;i++)
		{
			int a,b;
			cin>>a>>b;
			add(a,b);
			add(b,a);///无向图
		}
		dfs(1,-1);
		cout<<id<<" "<<maxx<<endl;
	}		
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43179892/article/details/83905090