树的直径
概念
给定一棵树,树中每个边都有权值,树中两点之间的距离定义为连接两点的路径边权和.树中最远的两点之间的距离称之为树的直径,也就是树的最长链(偶尔也指代为一条路径).
树的直径有两种求法,都是O(n)的.一种是基于树形dp(之后再补上).一种是基于搜索.
搜索
我们随便找一个节点作为根(之前如果有根就不用换).进行一次搜索,找到距离根最远的节点p,然后再将p作为起点,寻找一个距离p最远的点q,那么pq就是直径.
简单证明:
假设根节点是直径的一端,那么根据直径的定义,p一定是直径的一端.
假设根节点不是直径的一端,而且找到的最远的p也不是直径的一端,假设直径的一端是p’.另一个端点是q,那么p’到q的距离可以拆分成 p’到根,根到q,如果将前半截更新成p到根,结果不会缩小.反证法得出p一定是直径(之一)的一端.
其实这里跑最短路也行,只不过搜索的复杂度是O(|V|+|E|)的.(参考<算法与实现>)
模板
此题最后输出的是一半的直径(向上取整)
// 将代码中的dfs和bfs替换不会影响结果,dfs第二个参数填0
#include<bits/stdc++.h>
using namespace std;
#define MAXM 200001
struct NODE {
int w;
int e;
int next; //next[i]表示与第i条边同起点的上一条边的储存位置
} edge[MAXM];
int cnt;
int head[MAXM];
void CFSInit() {
// 链式前向星初始化
cnt = 1;
memset(head, 0, sizeof(head));
}
void add(int u, int v, int w) {
edge[cnt].w = w;
edge[cnt].e = v; //edge[i]表示第i条边的终点
edge[cnt].next = head[u]