树的直径算法
树上任意两节点之间最长的简单路径即为树的「直径」。记 为树的真实直径。
算法1,两遍dfs
首先从任意节点 开始进行第一次 DFS,到达距离其最远的节点,记为 ,然后再从 开始做第二次 DFS,到达距离 最远的节点,记为 ,则 即为树的直径。
那么为什么此算法可行?
我们只需证明对于任意一点 经过第一次dfs之后到达的点 一定是 的一端 或 。
定理:在一棵树上,从任意节点 开始进行一次 DFS,到达的距离其最远的节点 必为直径的一端。
我们采用反证法。记出发点为 ,真实直径 ,设从 进行的第一次dfs到达的距离其最远的点 不为 或 。
那么有三种情况:
- 若 在 上:那么 矛盾。其中 是 上一中转点
- 若 不在直径上,且 与 有重合路径
那么 矛盾。- 若 不在 上,且 与 不存在重复路径:
那么 矛盾。- 三种情况都矛盾,得证。
注意负权边不适用
那么代码实现就很简单了
const int N = 10005;
int n, c, d[N];
vector<int> e[N];
void dfs(int u, int fa) {
for (int v :e[u]) {
if (v == fa) continue;
d[v] = d[u] + 1;
if (d[v] > d[c]) c = v;
dfs(v, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
dfs(1, 0);
d[c] = 0, dfs(c, 0);
printf("%d\n", d[c]);
return 0;
}
?
树形dp
每个节点作为子树的根向下,所能延伸的最远距离 ,和次远距离 ,那么直径就是所有 的最大值。
树形 DP 可以在存在负权边的情况下求解出树的直径。
const int N=10010,M=20010;
int n,a,b,c,ans;
struct edge{int v,w;};
vector<edge> e[N];
int dfs(int x,int fa){
int d1=0,d2=0;
for(auto ed : e[x]){
int y=ed.v, z=ed.w;
if(y==fa) continue;
int d=dfs(y,x)+z;
if(d>=d1) d2=d1,d1=d;
else if(d>d2) d2=d;
// printf("回%d d1=%d d2=%d\n",x,d1,d2);
}
ans=max(ans,d1+d2);
// printf("离%d ans=%d\n",x,ans);
return d1;
}
int main(){
cin>>n;
for(int i=1; i<n; i++){
cin>>a>>b>>c;
e[a].push_back({b,c});
e[b].push_back({a,c});
}
dfs(1,-1);
cout << ans << endl;
return 0;
}
最后的最后,始终要记住,树是无环的,这一性质很重要,在两个算法中都用到了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现