树上问题——树的直径

树的直径的含义

树的直径就是树中所有最短路经距离的最大值。

求树的直径通常有两种方法,一种是通过两次搜索(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]);//在搜索过程中找到树的直径
	}
}

猜你喜欢

转载自blog.csdn.net/qq_57109165/article/details/126708979