[图论]------[树形结构]------树的直径

树的直径的定义

树的直径定义为树上距离最大的两个点的距离。常见的求法有两种。

方法一: 两次DFS

两次DFS的求法比较好理解,写在前面。
证明一个结论:树上任意一个结点能到达的最远的结点,一定是树的直径的一个端点。
设任意结点为 s ,s 能到达的最远节点为 t。他们的距离为 d i s [ s ] [ t ] dis[s][t] dis[s][t]
利用反证法,假设树的直径的两个端点为 n1,n2 且( n 1 ! = t 且 n 2 ! = t n1!=t且n2!=t n1!=tn2!=t)树上直径为 d i s [ n 1 ] [ n 2 ] dis[n1][n2] dis[n1][n2],t 与 n2 必然存在公共祖先 V。有 d i s [ s ] [ t ] = d i s [ s ] [ V ] + d i s [ V ] [ t ] dis[s][t]=dis[s][V]+dis[V][t] dis[s][t]=dis[s][V]+dis[V][t] d i s [ n 1 ] [ n 2 ] = d i s [ n 1 ] [ V ] + d i s [ V ] [ n 2 ] dis[n1][n2]=dis[n1][V]+dis[V][n2] dis[n1][n2]=dis[n1][V]+dis[V][n2]
由n1,n2是树上直径可知: d i s [ V ] [ n 2 ] > d i s [ V ] [ t ] dis[V][n2]>dis[V][t] dis[V][n2]>dis[V][t],所以 d i s [ s ] [ V ] + d i s [ V ] [ n 2 ] > d i s [ s ] [ V ] + d i s [ V ] [ t ] dis[s][V]+dis[V][n2]>dis[s][V]+dis[V][t] dis[s][V]+dis[V][n2]>dis[s][V]+dis[V][t],所以 d i s [ s ] [ n 2 ] > d i s [ s ] [ t ] dis[s][n2]>dis[s][t] dis[s][n2]>dis[s][t] 与 t 是 s 能到达的最远节点矛盾,原命题成立。
知道这个结论再求树的直径就简单了,先随意找一个节点进行DFS,找到最远节点,再对这个最远节点进行一次DFS,再次找最远结点,两个最远节点就是直径的两个端点。

代码:

void dfs(int u, int fa, int d)
{
	dis[u] = d;
	if(dis[u]>maxdis)
	{
		maxdis = dis[u];
		node1 = u;             //node1保存树的直径的一个端点
	}
	for(int i=head[u];i;i=edge[i].next)
	{
		int v = edge[i].to;
		if(v==fa)
		    continue;
		dfs(v, u, d+edge[i].dis);
	}
}

方法二:树上DP

我们假设树的根是rt
那么树的直径显然会有两种情况

  1. 直径的两个端点分别在rt的两个子树内
  2. 直径在rt的某个子树内
    这里就可以用到分治的思想,先以rt为根处理情况1,再递归rt的子树将情况2转变为情况1处理

d p [ u ] dp[u] dp[u]表示从结点u出发向u的子树走能走到的最远距离
v i vi vi为 u 的子节点。
那么有 d p [ u ] = m a x ( d p [ v i ] + d i s ( u , v i ) ) dp[u]=max(dp[v_i]+dis(u, v_i)) dp[u]=max(dp[vi]+dis(u,vi))

对于以u为根的子树内,经过结点u的最长链长度 m x l e n [ u ] mxlen[u] mxlen[u],有 m x l e n [ u ] = m a x ( d p [ v i ] + d p [ v j ] + d i s ( u , v i ) + d i s ( u , v j ) ) mxlen[u]=max(dp[v_i]+dp[v_j]+dis(u, v_i)+dis(u, v_j)) mxlen[u]=max(dp[vi]+dp[vj]+dis(u,vi)+dis(u,vj))

由于dfs的特性,这里我们并不需要 O ( n 2 ) O(n^2) O(n2)枚举,假设当前搜到的 u u u 的子节点为 v i v_i vi,那么显然 d p [ u ] dp[u] dp[u]已经被 d p [ v 1 ] 到 d p [ v i − 1 ] dp[v_1]到dp[v_{i-1}] dp[v1]dp[vi1]中最大的一个更新过,所以这里可以直接用已有的 d p [ u ] dp[u] dp[u] 代替另一个子节点的枚举。
最后整个树的最长链就是 m a x ( m x l e n ( u ) ) , ( 1 < = u < = N ) max(mxlen(u)) ,(1<=u<=N) max(mxlen(u))(1<=u<=N)

代码:

void DP(int u, int pa)
{
	dp[u] = 0;
	for(int i=head[u];i;i=edge[i].next)
	{
		int v = edge[i].to;
		if(v==pa) 
		    continue;
		DP(v, u);
		mxlen = max(mxlen, dp[u]+dp[v]+edge[i].dis);
		dp[u] = max(dp[u], dp[v]+edge[i].dis);
	} 
}

例题

模板1: POJ 2631 Roads in the North.

模板2: POJ 1985 Cow Marathon.

树的直径+思维: 洛谷P5536 【XR-3】核心城市.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值