洛谷B4016 树的直径题解(3种解法递进)
1.原题和原题链接
B4016 树的直径
题目描述
给定一棵 n n n 个结点的树,树没有边权。请求出树的直径是多少,即树上最长的不重复经过一个点的路径长度是多少。
输入格式
第一行输入一个正整数 n n n,表示结点个数。
第二行开始,往下一共 n − 1 n-1 n−1 行,每一行两个正整数 ( u , v ) (u,v) (u,v),表示一条边。
输出格式
输出一行,表示树的直径是多少。
输入输出样例 #1
输入 #1
5
1 2
2 4
4 5
2 3
输出 #1
3
说明/提示
数据保证, 1 ≤ n ≤ 10 5 1 \leq n \leq 10^5 1≤n≤105。
2.主要思路以及AC代码
1.第一种解法及其AC代码
主要思路1
这道题求树的直径,最容易理解的方法就是使用两次dfs。核心原理很简单:先随便选一个点跑dfs,找到离它最远的节点 x;再以 x 为起点跑一次dfs,找到离 x 最远的节点 y,x 到 y 的距离就是树的直径。我们可以使用动态数组存树,dfs 记录当前的节点深度,并不断更新最大深度和对应节点。第一次 dfs 找到直径一端,第二次直接算出直径长度。2次dfs可以在同一个dfs里面完成,相较于后面的2种写法更容易理解。
AC代码1
#include<bits/stdc++.h>
using namespace std;
int n,mh=-(1<<30),x;
vector<int> g[1000005];
void dfs(int k,int fa,int h){
if (h>mh){mh=h;x=k;}
for(int i=0;i<g[k].size();i++){
int v=g[k][i];
if(v==fa) continue;
dfs(v,k,h+1);
}
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int k,v;cin>>k>>v;
g[k].push_back(v);
g[v].push_back(k);
}
dfs(1,0,0);mh=-(1<<30);dfs(x,0,0);cout<<mh;
return 0;
}
2.第二种解法及其AC代码
主要思路2(1->2)
第二种是树形 dp的 解法,从两次 dfs改造而来:放弃两次遍历,只用一次dfs 遍历整棵树。上一种解法需要跑两遍dfs,这种方法在递归时,用 dp [k][0] 存节点 k 的最长子树深度,dp [k][1] 存次长子树深度。遍历每个子节点时,更新这两个值,直径就是每个节点的最长 + 次长子树深度之和。
对比优势(1->2)
只需要一次递归遍历,少一次全局搜索,且不容易出错,效率和第一种一样,但代码更精简,理解之后写起来会更快。
AC代码2
#include<bits/stdc++.h>
using namespace std;
int n,dp[100005][3],mh=0;
vector<int> g[100005];
void dfs(int k,int fa){
dp[k][0]=dp[k][1]=0;
for(int i=0;i<g[k].size();i++){
int v=g[k][i];
if(v==fa) continue;
dfs(v,k);
if ((dp[v][0]+1)>dp[k][0]){
dp[k][1]=dp[k][0];
dp[k][0]=dp[v][0]+1;
}
else if ((dp[v][0]+1)>dp[k][1]) dp[k][1]=dp[v][0]+1;
}
mh=max(mh,dp[k][0]+dp[k][1]);
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int k,v;cin>>k>>v;
g[k].push_back(v);
g[v].push_back(k);
}
dfs(1,0);cout<<mh;
return 0;
}
3.第三种解法及其AC代码
主要思路3(2->3)
第三种同解法2一样是树形 dp,但是第二种双层数组写法的极致简化:把 dp [k][0] 和 dp [k][1] 合并成单个 dp 数组。遍历子节点时,先用当前 dp [k](还未更新的最长深度)+dp [v]+1 更新直径,再更新 dp [k] 为最大深度,直接用一个变量完成两种深度的计算。
对比优势(2->3)
代码量最少,空间占用更小,计算步骤也更少,运行效率依旧和前两种一致,但考场写这个速度最快,是这3种解法里面最优的。(当然还是比较推荐解法1的,毕竟最容易理解)
AC代码3
#include<bits/stdc++.h>
using namespace std;
int n,dp[100005],mh=0;
vector<int> g[100005];
void dfs(int k,int fa){
dp[k]=0;
for(int i=0;i<g[k].size();i++){
int v=g[k][i];
if(v==fa) continue;
dfs(v,k);
mh=max(mh,dp[k]+dp[v]+1);
dp[k]=max(dp[k],dp[v]+1);
}
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int k,v;cin>>k>>v;
g[k].push_back(v);
g[v].push_back(k);
}
dfs(1,0);cout<<mh;
return 0;
}
以上就是本篇全部内容,感谢浏览!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)