树形dp入门

22 篇文章 0 订阅
1树的重心

树的重心,也叫树的质心。对于一棵树来说,删去该树的重心后,所有的子树的大小不会超过原树大小的二分之一。树的重心还有一个性质,是相对于树上的其他点而言的,就是删去重心后形成的所有子树中最大的一棵节点数最少。换句话说,就是删去重心后生成的多棵子树是最平衡的。一棵树的重心至多有两个。
两种处理方法

  1. 找最大子树siz最小的节点为重心
  2. 找最大子树<=n/2的节点为重心(好写)
POJ1655

求字典序最小的重心及其最大子树的size

const int N=20100;
const int M=40100;
int head[N],cnt;
struct node
{
	int nxt,v;
}edge[M];
void add(int u,int v)
{
	cnt++;
	edge[cnt].v=v;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int siz[N];
int root,min_maxss,n;
void init()
{
	cnt=0;min_maxss=inf;
	memset(head,0,sizeof(head));
	memset(siz,0,sizeof(siz));
}
void dfs(int u,int fa)
{
	siz[u]=1;int maxss=0;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa) continue;
		dfs(v,u);
		siz[u]+=siz[v];
		maxss=max(maxss,siz[v]);
	}
	maxss=max(maxss,n-siz[u]);
	if(maxss<min_maxss||(maxss==min_maxss)&&(u<root))
	{
		root=u;
		min_maxss=maxss;
	}
}
int main()
{
	int T=read();
	int u,v;
	while(T--)
	{
		init();n=read();
		for(int i=1;i<=n-1;i++)
		{
			u=read();v=read();
			add(u,v);add(v,u);
		}
		dfs(1,0);
		cout<<root<<' '<<min_maxss<<endl;
	}
	return 0;
}
树的直径

算法1:我们任取树中的一个节点x,找出距离它最远的点y,那么点y就是这棵树中一条直径的一个端点。我们再从y出发,找出距离y最远的点就找到了一条直径。这个算法依赖于一个性质:对于树中的任一个点,距离它最远的点一定是树上一条直径的一个端点。

算法2:首先,先将无根树转成有根树,定义F[i]表示从i出发向远离根节点的方向走的最长路径的长度,G[i]表示从i向远离根节点的方向走的次长路径的长度。注意F[i]和G[i]不能沿着i的同一个儿子走。特别地,如果i只有一个儿子,那么G[i]=0。答案为max(F[i]+G[i])。

void DFS(int x){
	f[x]=g[x]=0;
	for(int i=0;i<G[x].size();i++){
		Node v=G[x][i];
		if (!vist[v.to]){
			vist[v.to]=true;
			DFS(v.to);//访问子节点。
			vist[v.to]=false;
			if (f[x]<f[v.to]+v.val){//如果发现了一条更长的路径,那么更新f[x]和g[x]。
				g[x]=f[x];//原来的f[x]变为次长路,新发现的记为最长路。
				f[x]=f[v.to]+v.val;
			}
			else if (g[x]<f[v.to]+v.val) g[x]=f[v.to]+v.val;
			//如果找到了一条比次长路更长的路径,那么更新g[x]。
		}
	}
	ans=max(ans,f[x]+g[x]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值