树的直径
定义:
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个 数值概念,也可代指一条路径
树的直径通常有两种求法,时间复杂度均为O(n)。我们假设树以N个点N-1条边的无向图形式给出,并存储在邻接表中。
法一:树上DP
优点:1.代码简单
2.可以处理出当前点为根时每个子树的最长链。
缺点:不易求出直径的两个端点。
法二:两遍DFS(BFS)
优点:容易计算出直径上的具体节点。
缺点:只能求直径和距离。
证明就不再赘述,比较简单,网上也都有。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; #define R register #define ll long long inline ll read() { ll aa=0;char cc=getchar(); while(cc<'0'||cc>'9')cc=getchar(); while(cc>='0'&&cc<='9') {aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();} return aa; } const int N=1e6; int tot,ver[N<<1],nxt[N<<1],edge[N<<1],first[N]; inline void add(int x,int y,int z) { ver[++tot]=y;nxt[tot]=first[x]; edge[tot]=z;first[x]=tot;return; } int n,f[N],ans; void dp(int x,int fa) { f[x]=0; for(R int i=first[x],v;i;i=nxt[i]){ v=ver[i];if(v==fa)continue; dp(v,x); f[0]=max(f[0],f[x]+f[v]+edge[i]); f[x]=max(f[x],f[v]+edge[i]); } } int dis[N],pi,pj; void dfs(int x,int fa) { for(R int i=first[x],v;i;i=nxt[i]){ v=ver[i];if(v==fa)continue; dis[v]=dis[x]+edge[i];dfs(v,x); if(dis[pj]<dis[v])pj=v; } } queue<int>qi; void bfs(int x) { memset(dis,-1,sizeof(dis));dis[x]=0; qi.push(x); while(!qi.empty()){ int u=qi.front();qi.pop(); for(R int i=first[u],v;i;i=nxt[i]){ v=ver[i];if(dis[v]!=-1)continue; dis[v]=dis[u]+edge[i];qi.push(v); if(dis[v]>dis[pj])pj=v; } } } int main() { n=read();read(); for(R int i=1,x,y,z;i<n;++i){ x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } f[0]=0;dp(1,1);ans=f[0];//dp dis[1]=0;pj=1;dfs(1,1);pi=pj; dis[pi]=0;dfs(pi,pi);ans=dis[pj];//dfs pj=1;bfs(1);pi=pj; bfs(pi);ans=dis[pj];//bfs printf("%d\n",ans); }