树的直径的含义
树的直径就是树中所有最短路经距离的最大值。
求树的直径通常有两种方法,一种是通过两次搜索(bfs和dfs均可),另一种就是通过树形dp来求了。
dfs(bfs)求树的直径
随便从一个点 a a a开始,用dfs找到离初始点最远的点 b b b。然后再以最远点 b b b为初始点用dfs找到离初始点最远的点 c c c。
路径 b c bc bc即为当前树的直径。
int e,maxv=-1;
void dfs(int x,int father)
{
for(int i=h[x];i!=-1;i=ne[i])//用链式前向星存树
{
int j=e[i];
if(j==father) continue;//树是一种有向无环图,只要搜索过程中不返回父亲节点即可保证不会重复遍历同一个点。
d[j]=d[x]+w[i];//更新每个点到起始点的距离(在树中任意两点的距离是唯一的)
if(d[j]>maxv) maxv=d[j],e=j;//更新最远的点的信息
dfs(j,x);//继续搜索下一个节点
}
}
树形dp求树的直径
首先递归到叶结点,然后再由叶结点连接父节点,更新 f 1 , f 2 f1,f2 f1,f2数组( f 1 [ i ] f1[i] f1[i]数组表示离点i最远的点的距离, f 2 [ i ] f2[i] f2[i]数组表示离点i次远的点的距离)。
最后找到任意一点的 f 1 + f 2 f1+f2 f1+f2最大的值,该值即为树的直径。
void dp(int x,int father)
{
for(int i=h[x];i!=-1;i=ne[i])链式前向星存树
{
int j=e[i];
if(j==father) continue;//防止原路返回
dp(j,x);//dp过程应该是由叶节点开始的,也就是说先递归到叶节点再开始进行状态转移
if(f1[x]<f1[j]+w[i])//如果子节点的最大距离+子节点与父节点之间边的权重大于父节点的最大距离,那么父节点的最大距离和次大距离都要得到相应更新
{
f2[x]=f1[x];
f1[x]=f1[j]+w[i];
}
else if(f2[x]<f1[j]+w[i])//若子节点的最大距离+子节点与父节点之间边的权重小于父节点的最大距离,再判断与父节点的次大距离的关系
f2[x]=f1[j]+w[i];
ans=max(ans,f1[x]+f2[x]);//在搜索过程中找到树的直径
}
}