填坑行动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;
}
posted @ 2021-02-15 21:32  jiangtaizhe001  阅读(231)  评论(0编辑  收藏  举报