Balancing Act POJ - 1655 树形dp

Balancing Act POJ - 1655

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

题意:题目要求给定一颗树,删去任意一点,使得要最大子树最小,输出改点的编号和子树大小。

这是一道求树重心的题。用到一点树形dp,也可以说是dfs。类似于树链剖分找重儿子。

不知道树的重心的同学,我这里简单讲解下:

首先 重心的含义:树的重心也叫树的质心。找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。(来自百度)

其次 我们为什么要找树的重心,其实如果对于一颗完全二叉树或者平衡二叉树来说,树已经很优雅了,我们直接树上处理就ok了,。那么,引入重心是为了解决树不平衡,不美观的情况,这个打个比方

给出几组边:
1 2
2 3
3 4
在这里插入图片描述
如果树的形状是链型的,那么我们从更节点遍历下去,复杂度将会是O(n)
这并不是我们想要的。
稍微想一想,对于一颗无限树,我们的根是可以自定义的,比如上图,我们把3号节点作为根节点,那么树不就变成了这样:
在这里插入图片描述

OK 我们把3号节点 叫做重心,满足重心的定义。

如何找重心,下面代码有详细注解:

AC代码:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <functional>
#include <cmath>
#define il inline
#define INF (1<<30)
using namespace std;
int const N=20000+100;
int head[N<<1],t,n,u,v,cnt;
int siz[N<<1];//删除u节点后以i为根的最大节点数
bool vis[N<<1];//是否被删除 
int Max,pos;
struct Edge
{
	int e;
	int to;	
}edge[N<<1];
void add(int u,int v)
{
	edge[cnt].e=v;
	edge[cnt].to=head[u];
	head[u]=cnt++;
}
void Dbug(int u)//dug用 
{
	for(int i=head[u];i!=-1;i=edge[i].to)
	{
		cout<<u<<"->"<<edge[i].e<<endl;
	}
}
void dfs(int u)
{
	siz[u]=1; //当前节点为数量为1 
	vis[u]=true;
	int maxn=0;
	for(int i=head[u];i!=-1;i=edge[i].to)
	{
		int v=edge[i].e;
		if(vis[v])continue;
		dfs(v);//一直往下走 
		siz[u]+=siz[v];//返回时,加上儿子节点的数量 
		maxn=max(maxn,siz[v]);//先判定儿子节点的数量最大 
	}
	maxn=max(maxn,n-siz[u]);
	//再判定当前节点剩余节点的数量,用总数n-当前节点,
	//相当于把u节点作为根节点
	//,这里和上面的maxn一起结合理解 
	if(maxn<Max)//如果比Max小 
	{//记录重心位置和节点数 
		pos=u;
		Max=maxn;
	}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		//初始化 
		memset(head,-1,sizeof(head));
		memset(siz,0,sizeof(siz));
		memset(vis,false,sizeof(vis));
		cnt=0;
		scanf("%d",&n);
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v),add(v,u);	//采用链式前向星存边 
		}
		Max=INF;//记录最大子树节点数最小 
		pos=0;//记录重心的位置 
		dfs(1);
		printf("%d %d\n",pos,Max);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值