定义:
树的直径的定义:
在一棵树中,每一条边都有权值,树中的两个点之间的距离,定义为连接两点的路径上边权之和,那么树上最远的两个点,他们之间的距离,就被称之为,树的直径。
树的直径的别称,树的最长链。
请注意:树的直径,还可以认为是一条路径,不一定是只是一个数值。
树的直径一般有两种求解方法:
1、两次搜索(BFS和DFS都可以)
优点 : 可以通过一个新的数组记录路径信息(例如父节点与子节点之间的关系)
缺点 : 无法处理 负边权(遇到 负边权 凉凉)
思路:先任选一点,搜索找到距离该点最远的的点,该点必是树的直径的端点之一,然后以该点为起点,找到距离该点最远的点,即为树的直径。
证明在这里或者这里
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
ll head[300000];
struct node
{
ll to,pre,w;
} edge[500000];
ll dist[300000];
ll cnt=1,maxx;
void add(ll u,ll v,ll w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].pre=head[u];
head[u]=cnt++;
}
ll dfs(ll u,ll father,ll distance)
{//u是当前节点,father是其父节点,distance是u到源点的距离
dist[u]=distance;
for(ll i=head[u]; i!=-1; i=edge[i].pre)
{//向下寻找子节点
ll v=edge[i].to;
if(v!=father)
{
dfs(v,u,distance+edge[i].w);
}
}
}
int main()
{
ll n,u,v,w;
memset(head,-1,sizeof(head));
scanf("%lld",&n);
for(ll i=1; i<=n-1; i++)
{
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,-1,0);//第一个点没有父节点,所以为-1
u=1;
for(ll i=1; i<=n; i++)
{
if(dist[i]>dist[u])
{
u=i;
}
}
dfs(u,-1,0);
for(ll i=1; i<=n; i++)
{
if(dist[i]>dist[u])
{
u=i;
}
}
printf("%lld\n",dist[u]);
return 0;
}
2、树形dp
证明在这里或者这里
优点 : 可以有效处理 负边权
缺点 : 对于记录路径的信息效率较低
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
ll head[300000];
struct node
{
ll to,pre,w;
} edge[500000];
ll dist[300000];
ll cnt=1,maxx;
void add(ll u,ll v,ll w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].pre=head[u];
head[u]=cnt++;
}
ll dp(ll u,ll father)
{
for(ll i=head[u]; i!=-1; i=edge[i].pre)
{
ll v=edge[i].to;
if(v!=father)
{
dp(v,u);
maxx=max(maxx,dist[u]+dist[v]+edge[i].w);
dist[u]=max(dist[u],dist[v]+edge[i].w);
}
}
}
int main()
{
ll n,u,v,w;
memset(head,-1,sizeof(head));
scanf("%lld%lld",&n,&k);
for(ll i=1; i<=n-1; i++)
{
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dp(1,-1);
printf("%lld\n",maxx);
return 0;
}