树的直径
讲课
一、树的直径
树上两点的距离:从树上一点到另一点所经过的权值
当树上两点距离最大时,就称作树的直径
树的直径既可以指这个权值,也可以指这个路径 (路径也叫树的最长链)。树的直径有两种三种方法,都是O(n)
最笨的方法就是对每一个结点进行 BFS,因为 BFS 有这个性质:BFS 生成的
广度优先树的每一个结点到达根结点的路径总是最短路。这样,把每一个结点 BFS
一遍就会生成一个该结点到达的最远结点。按照定义取出最长的路径即可。由于 BFS 时间复杂度是 O(N),这个方法的时间复杂度是O(N^2)。
1,两次BFS或dfs
贪心求直径的方法是任意找一个点为根,dfs整棵树找到距离他最远的点x,再以这个点x为根求出距离它最远的点y,(x,y)即为直径。(注意:这种方法只能求非负边权)
定理:在一个连通无向无环图中,以任意结点出发所能到达的最远结点,一定是该图直径的端点之一。
证明:假设这条直径是δ(s,t)。
分两种情况:
1.当出发结点 y 在δ(s,t)时,假设到达的最远结点 z 不是 s,t 中的任一个。这时将δ(y,z)与不与之重合的δ(y,s)拼接,可以得到一条更长的直径,与前提矛盾。
2.当出发结点 y 不在δ(s,t)上时,分两种情况:
1). 当 y 到达的最远结点 z 横穿δ(s,t)时,记与之相交的结点为 x。此时有δ(y,z)=δ(y,x)+δ(x,z)。而此时δ(y,z)>δ(y,t),故可得δ(x,z)>δ(x,t)。该假设不成立。
2).当 y 到达的最远结点 z 与δ(s,t)不相交时,当 y 到达的最远结点 z 与δ(s,t)不相交时,记 y 到 t 的最短路首先与δ(s,t)相交的结点是 x。由假设δ(y,z)>δ(y,x)+δ(x,t)。而δ(y,z)+δ(y,x)+δ(x,s)又可以形成δ(z,s)可知不成立
所以定理成立
1、直径两端点一定是两个叶子节点
2、距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出
3、对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为(x,y),用一条边将两棵树连接,那么新树的直径一定是u,v,x,y,中的两个点
证明:如果新树直径不是原来两棵树中一棵的直径,那么新直径一定经过两棵树的连接边,新直径在原来每棵树中的部分一定是距离连接点最远的点,即一定是原树直径的一个端点。
4、对于一棵树,如果在一个点的上接一个叶子节点,那么最多会改变直径的一个端点 证明:假设在x下面接一个点y,直径变成了(u,x),原树直径为(a,b),那么dis(u,x)>dis(a,b),dis(u,x)=dis(u,y)+1,dis(u,x)=dis(u,y)+1,即dis(u,y)+1>dis(a,b),如果dis(u,y)<dis(a,b),那么显然不成立;如果dis(u,y)=dis(a,b),那么(u,y)也是原树的直径,符合上述结论。
5、若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点
dfs
int dfs(int u,int fa){ if(sum<dis[u]){ sum=dis[u]; t=u; } for(int i=head[u];i;i=e[i].nxt){ int to=e[i].v; if(to!=fa){ pre[to]=i; dis[to]=dis[u]+e[i].w; dfs(to,u); } } }
bfs
void bfs(int x){ memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); queue<int>q; q.push(x); vis[x]=1; while(q.size()){ int now=q.front(); q.pop(); for(int i=head[now];i;i=e[i].nxt){ int to=e[i].v; if(!vis[to]){ pre[to]=now; dis[to]=dis[now]+1; vis[to]=1; q.push(to); } } } t=0; for(int i=1;i<=n;i++){ if(dis[i]>t){ t=dis[i],f[0]=i; } } }
dp
int dp(int u){ vis[u]=1; for(int i=head[u];i;i=e[i].nxt){ int to=e[i].v; if(vis[to])continue; dp(to); sum=max(sum,f[u]+f[to]+e[i].w); f[u]=max(f[to]+e[i].w,f[u]); } }