填坑行动7-树的直径及求出树的直径路径
树的直径的定义
树的直径是树里面最长的一条链。树的直径不仅仅只有一条。
求树的直径有两种方法:搜索和树形DP。这里主要介绍树形DP。
求出树的直径的长度
方法一、dfs大法
这里不做过多的介绍,主要是记录下一某个点为端点的最长路径和次长路径就可以了。
方法二、树形DP
这里令\(f_i\)是一棵以\(i\)为根节点的子树的深度。
如果节点\(i\)的孩子为\(a_i\left(1 \leq i \leq n\right)\)那么,以\(i\)为节点的这棵树的直径就是\(\max \left( f_{a_i}+f_{a_j}+v_i+v_j \right) \left(i {=}\mathllap{/\,} j \right)\),其中\(v_i,v_j\)是边权,无边权的时候直接当成\(1\)。当然,我们在搜索的时候需要枚举\(i\),也就是在dfs的时候要对每个节点进行判断,一般在回溯的时候进行判断。
代码如下:
#include<cstdio>//无权树
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#define maxn 300039
using namespace std;
int head[maxn],to[maxn],nex[maxn],k;
#define add(x,y); nex[++k]=head[x];\
to[k]=y;\
head[x]=k;
int n,m,x,y,z;
int maxx;
int f[maxn];
void dfs(int num,int pre){
f[num]=0;
for(int i=head[num];i!=-1;i=nex[i])
if(to[i]!=pre){
dfs(to[i],num);
maxx=max(maxx,f[num]+f[to[i]]+1);
f[num]=max(f[to[i]]+1,f[num]);
}
return;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
maxx=-10000000000;
dfs(1,0);
printf("%d",maxx);
return 0;
}
求出树的直径路径
方法一、dfs大法
只要记录下搜索的路径即可
方法二、树形DP
发现网上关于树形DP求直径路径的很少,这里就来一发。
我们令\(g_{i,j}\)来表示节点\(i\)中第\(j\)长边的下一个端点,往往\(j\)只需要记录\(1\)和\(2\)的值就可以了。
我们在更新\(f_i\)的时候就可以更新\(g_{i,j}\),只需要把原来的\(\max\)改成\(\operatorname{if}\) 就可以了。当然,在更新\(maxx\)的时候,我们还要更新一个变量\(node\),表示这棵树的直径是从\(node\)为中间的端点,相当于树的重心。如果要输出这个树的直径,我们可以先输出\(node\),然后从左边开始,输出\(g_{node,1}\),接着是\(g_{g_{node,1},1}\),直到输到底为止,然后输出\(g_{node,2}\),接着是\(g_{g_{node,1},2}\),但是要注意不是\(g_{g_{node,2},2}\)。
代码如下:
#include<cstdio> //求直径路径
#include<cstring>//DP
#define max(a,b) ((a)>(b)?(a):(b))
#define maxn 200039
using namespace std;
int head[maxn],to[maxn],nex[maxn],v[maxn],k;
#define add(x,y,z); nex[++k]=head[x];\
to[k]=y;\
head[x]=k;\
v[k]=z;
int n,m,x,y,z;
int maxx;
int f[maxn],g[maxn][3],node;
void dfs(int num,int pre){
f[num]=0;
int max1,max2;
max1=max2=0;
for(int i=head[num];i!=-1;i=nex[i])
if(to[i]!=pre){
dfs(to[i],num);
if(f[to[i]]+v[i]>max1){
max2=max1; g[num][2]=g[num][1];
max1=f[to[i]]+v[i];
g[num][1]=to[i];
}
else if(f[to[i]]+v[i]>max2){
max2=f[to[i]]+v[i];
g[num][2]=to[i];
}
f[num]=max(f[to[i]]+v[i],f[num]);
}
if(max1+max2>maxx){
maxx=max1+max2;
node=num;
}
return;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
maxx=-10000000000;
dfs(1,0);
int i;
//printf("%d",node);
i=node;
while(i){
printf("%d ",i);
i=g[i][1];
}
i=g[node][2];
while(i){
printf("%d ",i);
i=g[i][1];
}
printf("\n%d",maxx);
return 0;
}