树的直径定义:
树上任意两节点之间最长的简单路径即为树的直径。
求法:
(1)两次dfs:
先从任意一点x出发,找离它最远的点y,再从点y出发,找离它最远的点z,y到z的距离就是树的直径.
模板:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,m,tot,p,ans;
int d[N],head[N],e[N],w[N],ne[N];
//添加边
void add(int x,int y,int z){
tot++;//总数
e[tot]=y;//表示指向的点
w[tot]=z;//边权
ne[tot]=head[x];//邻接一下,next指针
head[x]=tot;//邻接表结点编号
}
void dfs(int x,int fa){//fa:x的父节点
int i,j;
if(ans<d[x]){
ans=d[x];
p=x;
}
for( i=head[x];i;i=ne[i]){
j=e[i];
if(j==fa) continue;
d[j]=d[x]+w[i];
dfs(j,x);//dfs j,x为父节点
}
}
void find(int x){
//每次find都需要清0,ans和d[x],从x开始,fa=0
ans=0;
d[x]=0;
dfs(x,0);
}
int main(){
scanf("%d %d",&n,&m);//n为点数,m为边数
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
//添加边
add(x,y,z);
add(y,x,z);
}
find(1);//先找到与1相隔最远的p,
find(p);//再从p出发找相隔最远的,此距离就是ans
printf("%d\n",ans);
return 0;
}
(2)树形dp:(可以再存在负边权的情况下求解出树的直径)
记录到1为树的根时,每个节点作为子树的根向下所能延伸的
最远距离d1和次远距离d2,那么直径就是所有d1+d2的最大值。
对于每个节点我们要记录两个值:
f1[i]表示以i为根的子树中,i到叶子节点距离的最大值;
f2[i]表示以i为根的子树中,i到叶子节点距离的次大值。
若j是i的儿子,那么
(1)若f1[i]<f1[j]+w[i][j] ,则f2[i]=f1[i],f1[i]=f1[j]+w[i][j]
(2)否则,若 f2[i]<f1[j]+w[i][j] ,则f2[i]=f1[j]+w[i][j]
模板:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n,m,tot,ans;
int f1[N],f2[N];//f1[i]表示以i为根的子树中,i到叶子节点距离的最大值;
int head[N],e[N],w[N],ne[N];//f2[i]表示以i为根的子树中,i到叶子节点距离的次大值。
void add(int x,int y,int z){//添加边
tot++;
e[tot]=y;
w[tot]=z;
ne[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int fa){
int i,j;
for(i=head[x];i;i=ne[i]){
j=e[i];//j为x的邻接点
if(j==fa) continue;//如果是直接连接,continue
dfs(j,x);//j的父节点是x //否则,继续深搜
if(f1[x]<f1[j]+w[i]){
f2[x]=f1[x];//次小值=f1[x]
f1[x]=f1[j]+w[i];//f1[x]更新为较大的值
}
else if(f2[x]<f1[j]+w[i]){//否则,在f1[x]保持是最大值的时候,更新f2[x]
f2[x]=f1[j]+w[i];
}
ans=max(ans,f1[x]+f2[x]);//更新ans
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1,0);//从1,父节点为0开始
printf("%d\n",ans);
return 0;
}