树的直径

定义

树的直径:树中最远的两个节点之间的距离被称为树的直径,连接这两个点的路径被称为树的最长链

性质

1. 直 径 两 端 点 一 定 是 叶 子 节 点 。 2. 距 任 意 点 最 远 点 一 定 是 直 径 的 端 点 , 据 所 有 点 最 大 值 最 小 的 点 一 定 是 直 径 的 中 点 。 3. 两 棵 树 相 连 , 新 直 径 的 两 端 点 一 定 是 原 四 个 端 点 中 的 两 个 4. 两 棵 树 相 连 , 新 直 径 长 度 最 小 为 m a x ( m a x ( 直 径 1 , 直 径 2 ) , 半 径 1 + 半 径 2 + 新 边 长 度 ) ( 设 k 为 直 径 中 最 接 近 中 点 的 节 点 , 半 径 = m a x ( t o t − d [ k ] , d [ k ] ) ) 5. 一 棵 树 上 接 一 个 叶 子 结 点 , 直 径 最 多 改 变 一 个 端 点 6. 若 一 棵 树 存 在 多 条 直 径 , 多 条 直 径 交 于 一 点 , 且 交 点 是 直 径 的 严 格 中 点 ( 中 点 可 能 在 某 条 边 内 ) 1.直径两端点一定是叶子节点。\\2.距任意点最远点一定是直径的端点,据所有点最大值最小的点一定是直径\\的中点。\\3.两棵树相连,新直径的两端点一定是原四个端点中的两个\\4.两棵树相连,新直径长度最小为\\max(max(直径1,直径2),半径1+半径2+新边长度 ) \\(设k为直径中最接近中点的节点,半径=max(tot-d[k],d[k]))\\5.一棵树上接一个叶子结点,直径最多改变一个端点\\6.若一棵树存在多条直径,多条直径交于一点,且交点是直径的严格中点\\(中点可能在某条边内) 1.2.3.4.max(max(12)1+2+)k=max(totd[k],d[k])5.6.

求法

我们假设树以n个点n-1条边的无向图给出,存储于邻接表中。

树形dp求树的直径:

该 无 向 图 可 以 看 作 以 某 一 节 点 为 根 的 有 根 树 , 设 以 1 号 节 点 为 根 , 设 D [ x ] 为 从 x 出 发 , 到 其 子 树 最 远 节 点 的 距 离 , 设 x 的 子 节 点 为 y 1 , y 2 , . . .   , y t , 则 有 D [ x ] = max ⁡ 1 < = i < = t { D [ y i ] + e d g e [ x ] [ y i ] } ‾ 接 下 来 考 虑 经 过 点 x 的 最 长 链 长 度 F [ x ] , 假 设 在 求 D [ x ] 的 过 程 中 我 们 将 要 枚 举 到 y i 节 点 , 则 此 时 D [ x ] 存 储 则 x 到 前 i − 1 颗 子 树 的 最 长 路 , 由 于 F [ x ] 必 然 经 过 D [ x ] 的 路 径 , 则 我 们 可 以 在 求 D [ x ] 的 过 程 中 不 断 用 D [ x ] + D [ y i ] + e d g e [ x ] [ y i ] 更 新 F [ x ] , 通 过 枚 举 经 过 所 有 点 的 链 的 最 长 链 即 可 求 出 树 的 直 径 . 算 法 复 杂 度 : O ( n ) ; 该无向图可以看作以某一节点为根的有根树,设以1号节点为根,设D[x]为从\\x出发,到其子树最远节点的距离,设x的子节点为y_1,y_2,... \ ,y_t,则有\\\underline{\mathbf{D[x] = \max_{1 <= i <= t}\left \{ D[y_i]+edge[x][y_i] \right \} }} \\接下来考虑经过点x的最长链长度F[x],假设在求D[x]的过程中我们将要枚举\\到y_i节点,则此时D[x]存储则x到前i-1颗子树的最长路,由于F[x]必然经过\\D[x]的路径,则我们可以在求D[x]的过程中不断用D[x]+D[y_i]+edge[x][y_i]\\更新F[x],通过枚举经过所有点的链的最长链即可求出树的直径.\\算法复杂度:O(n); ,1,D[x]x,,xy1,y2,... ,yt,D[x]=1<=i<=tmax{D[yi]+edge[x][yi]}xF[x],D[x]yi,D[x]xi1,F[x]D[x],D[x]D[x]+D[yi]+edge[x][yi]F[x],.:O(n);

void dp(int x)
{
	v[x] = 1;
	for(int i = head[x];i;i = next[i])
	{
		int y = ver[i],z = edge[i];
		if(v[y]) continue;
		dp(y);
		ans = max(ans,D[x]+D[y]+z);
		D[x] = max(D[x],D[y]+z);
	}
}

两次BFS/DFS求树的直径:

1. 从 任 意 节 点 出 发 , 通 过 B F S 或 D F S 找 出 距 离 该 点 最 远 的 点 p ; 2. 从 p 点 出 发 , 再 次 通 过 B F S 或 D F S 找 出 距 离 p 点 最 远 的 点 q ; 3. p 到 q 的 路 径 即 为 树 的 一 条 直 径 . 因 为 p 必 定 是 直 径 的 一 个 端 点 , 则 据 端 点 最 远 的 点 必 为 直 径 的 令 一 端 ; 算 法 限 制 : 仅 适 用 于 边 权 值 均 非 负 的 无 向 图 . 优 点 : 方 便 记 录 途 径 节 点 . 算 法 复 杂 度 : O ( n ) . 1.从任意节点出发,通过BFS或DFS找出距离该点最远的点p;\\2.从p点出发,再次通过BFS或DFS找出距离p点最远的点q;\\3.p到q的路径即为树的一条直径.因为p必定是直径的一个端点,\\则据端点最远的点必为直径的令一端;\\{\color{Blue} 算法限制:仅适用于边权值均非负的无向图.} \\{\color{Blue}优点:方便记录途径节点.}\\算法复杂度:O(n). 1.,BFSDFSp;2.p,BFSDFSpq;3.pq.p,;:.:便.:O(n).

例题:

acwing350. 巡逻

题目大意:
在一个地区有 n 个村庄,编号为 1,2,…,n。
有 n−1 条道路连接着这些村庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其他任一个村庄。
每条道路的长度均为 1 个单位。
为保证该地区的安全,巡警车每天都要到所有的道路上巡逻。
警察局设在编号为 1 的村庄里,每天巡警车总是从警局出发,最终又回到警局。
为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路,每条新道路可以连接任意两个村庄。
两条新道路可以在同一个村庄会合或结束,甚至新道路可以是一个环。
因为资金有限,所以 K 只能为 1 或 2。
同时,为了不浪费资金,每天巡警车必须经过新建的道路正好一次。
编写一个程序,在给定村庄间道路信息和需要新建的道路数的情况下,计算出最佳的新建道路的方案,使得总的巡逻距离最小。

由 于 每 条 道 路 都 至 少 经 过 一 次 , 且 需 要 返 回 原 点 ; 1. K = 0 时 , 需 要 经 过 的 距 离 a n s = 2 ∗ ( n − 1 ) , 即 每 条 道 路 都 经 过 了 两 次 ; 2. k = 1 时 , 只 需 要 将 图 的 直 径 两 端 连 接 起 来 , 直 径 之 间 的 路 即 只 需 要 走 一 次 , 设 直 径 两 端 的 距 离 为 L 1 , 则 此 时 a n s = 2 ∗ ( n − 1 ) − L 1 + 1 ; 3. k = 2 时 , 求 情 况 2 下 的 另 一 个 环 , 两 个 环 不 重 叠 部 分 只 需 要 走 一 次 , 而 简 单 推 导 可 知 两 个 环 重 叠 部 分 的 路 需 要 走 两 次 , 此 时 我 们 可 以 在 计 算 了 树 的 直 径 L 1 的 后 , 将 其 路 径 上 的 边 权 改 为 − 1 , 再 次 求 树 的 直 径 L 2 , 则 a n s = 2 ∗ ( n − 1 ) − ( L 1 − 1 ) − ( L 2 − 1 ) = 2 ∗ ( n − 1 ) − L 1 − L 2 ; 总 结 : 第 一 次 求 直 径 我 们 可 以 用 两 次 B F S 记 录 路 径 , 方 便 后 续 修 改 边 权 , 第 二 次 求 直 径 利 用 树 形 d p 求 有 负 权 边 情 况 下 树 的 直 径 , 代 码 如 下 : 由于每条道路都至少经过一次,且需要返回原点;\\1.K = 0时,需要经过的距离ans = 2*(n-1),即每条道路都经过了两次;\\2.k = 1时,只需要将图的直径两端连接起来,直径之间的路即只需要走一\\次,设直径两端的距离为L_1,则此时ans = 2*(n-1)-L_1+1;\\3.k = 2时,求情况2下的另一个环,两个环不重叠部分只需要走一次,而简\\单推导可知两个环重叠部分的路需要走两次,此时我们可以在计算了\\树的直径L_1的后,将其路径上的边权改为-1,再次求树的直径L_2,则\\ans = 2*(n-1)-(L_1-1)-(L_2-1) = 2*(n-1)-L_1-L_2;\\总结:第一次求直径我们可以用两次BFS记录路径,方便后续修改边\\权,第二次求直径利用树形dp求有负权边情况下树的直径,代码如下: ,;1.K=0,ans=2(n1),;2.k=1,,,L1,ans=2(n1)L1+1;3.k=2,2,,,L1,1,L2,ans=2(n1)(L11)(L21)=2(n1)L1L2;:BFS,便,dp,:

#include<bits/stdc++.h>
#define next ne
using namespace std;
typedef pair<int,int> P;
int head[100005],next[200005],ver[200005],L,id[100005],edge[200005],d[200005];
bool v[100005];
void init()
{
	//memset(head,0,sizeof(head));
	memset(id,0,sizeof(id));
	memset(v,0,sizeof(v));
	L = 0;
} 
void dp(int x)
{
	v[x] = 1;
	int ans;
	for(int i = head[x];i;i = next[i])
	{
		int y = ver[i];
		if(v[y]) continue;//不能从下往上算; 
		dp(y);
		L = max(L,d[x]+d[y]+edge[i]);
		d[x] = max(d[x],d[y]+edge[i]);
	}
} 
int bfs(int x)
{
	int ans;
	queue<P>q;
	q.push(P(x,0));
	while(!q.empty())
	{
		int now = q.front().first,l = q.front().second;
		q.pop();
		if(v[now]) continue;
		v[now] = 1;
		if(L < l) 
		{
			ans = now;
			L = l;
		}
		int pos = head[now];
		while(pos)
		{
			if(v[ver[pos]])
			{
				pos = next[pos];
				continue;
			}
			id[ver[pos]] = now;
			q.push(P(ver[pos],edge[pos]+l));
			pos = next[pos];
		}
	}
	return ans;
}
int main()
{
	int n,k;cin >> n >> k;
	for(int i = 1;i < n;++i)
	{
		int x,y;scanf("%d%d",&x,&y);
		next[2*i-1] = head[x];
		head[x] = 2*i-1;
		ver[2*i-1] = y;
		edge[2*i-1] = 1;
		next[2*i] = head[y];
		head[y] = 2*i;
		ver[2*i] = x;
		edge[2*i] = 1;
	}
	int ans = 2*(n-1);
	int p = bfs(1);
	init();
	int q = bfs(p);
	ans -= L-1;
	//cout << L << ' ';
	if(k^2)
	{
		printf("%d\n",ans);
		return 0;
	 } 
	for(int i = q;id[i];i = id[i])
	{
		int now = head[i];
		while(now)
		{
			if(ver[now] == id[i])
			{
				edge[now] = -1;
				break;
			}
			now = next[now];
		}
		now = head[id[i]];
		while(now)
		{
			if(ver[now] == i)
			{
				edge[now] = -1;
				break;
			}
			now = next[now];
		}
	}
	init();
	dp(1);
	ans -= L-1;
	cout << ans << endl;
	return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值