D47【模板】树的直径 B4016 树的直径

D47【模板】树的直径 B4016 树的直径_哔哩哔哩_bilibili

 

树的直径 - OI Wiki

树上任意两节点之间最长的简单路径即为树的「直径」.一棵树可以有多条直径,他们的长度相等.

两次 DFS 或 树形 DP 的方法在 𝑂(𝑛) 时间求出树的直径.两次 DFS,适合正边权.树形 DP,适合正负边权.

两次 DFS

首先从任意节点 𝑦 开始进行第一次 DFS,到达距离其最远的节点,记为 𝑧,然后再从 𝑧 开始做第二次 DFS,到达距离 𝑧 最远的节点,记为 𝑧′,则 𝛿(𝑧,𝑧′) 即为树的直径.

如果需要求出一条直径上所有的节点,则可以在第二次 DFS 的过程中,记录每个点的前序节点.

树形 DP

方法 1

记录当 1 为树的根时,每个节点作为子树的根向下,所能延伸的最长路径长度 𝑑1 与次长路径(与最长路径无公共边)长度 𝑑2,那么直径就是对于每一个点,该点 𝑑1 +𝑑2 能取到的值中的最大值.

如果需要求出一条直径上所有的节点,则可以在 DP 的过程中,记录下每个节点能向下延伸的最长路径与次长路径所对应的子节点,在求 𝑑 的同时记下对应的节点 𝑢

方法 2

定义 𝑑𝑝[𝑢] 为以 𝑢 为根的子树中,从 𝑢 出发的最长路径.得出转移方程:𝑑𝑝[𝑢] =max(𝑑𝑝[𝑢],𝑑𝑝[𝑣] +𝑤(𝑢,𝑣)),其中的 𝑣 为 𝑢 的子节点,𝑤(𝑢,𝑣) 表示所经过边的权重.

对于树的直径,实际上是可以通过枚举从某个节点出发不同的两条路径相加的最大值求出.因此,在更新 𝑑𝑝[𝑢] 之前,计算 𝑑 =max(𝑑,𝑑𝑝[𝑢] +𝑑𝑝[𝑣] +𝑤(𝑢,𝑣)) 即可算出直径 𝑑

性质

若树上所有边边权均为正,则树的所有直径中点重合.

B4016 树的直径 - 洛谷

// 树的直径 正边权 两次DFS O(n)
#include<bits/stdc++.h>
using namespace std;

const int N=100005;
int n,p,mxd,d[N];
vector<pair<int,int>> e[N];

void dfs(int u,int fa){
  if(d[p]<d[u]) p=u; //记录直径端点
  for(auto [v,w]:e[u])if(v!=fa){
    d[v]=d[u]+w; //记录从根到v的距离
    dfs(v,u);
  }
}
int main(){
  cin>>n;
  for(int i=1,x,y;i<n;i++){
    cin>>x>>y;
    e[x].emplace_back(y,1);
    e[y].emplace_back(x,1);
  }
  dfs(1,0); d[p]=0;   //p是直径的一个端点
  dfs(p,0); mxd=d[p]; //p是直径的另一个端点
  cout<<mxd;
}

 

// 树的直径 正负边权 树形DP O(n)
#include<bits/stdc++.h>
using namespace std;

const int N=100005;
int n,md,d[N],d2[N]; //d[u],d2[u] 从u向下走的最长,次长距离
vector<pair<int,int>> e[N];

void dfs(int u,int fa){
  for(auto [v,w]:e[u])if(v!=fa){
    dfs(v,u);
    if(d[u]<d[v]+w) d2[u]=d[u],d[u]=d[v]+w;
    else if(d2[u]<d[v]+w) d2[u]=d[v]+w;
  }
  md=max(md,d[u]+d2[u]);
}
int main(){
  cin>>n;
  for(int i=1,x,y;i<n;i++){
    cin>>x>>y;
    e[x].emplace_back(y,1);
    e[y].emplace_back(x,1);
  }
  dfs(1,0);
  cout<<md;
}

 

// 树的直径 正负边权 树形DP O(n)
#include<bits/stdc++.h>
using namespace std;

const int N=100005;
int n,md,d[N]; //d[u]从u点向下走的最长距离
vector<pair<int,int>> e[N];

void dfs(int u,int fa){
  for(auto [v,w]:e[u])if(v!=fa){
    dfs(v,u);
    md=max(md,d[u]+w+d[v]); //拼凑直径
    d[u]=max(d[u],d[v]+w);  //更新d[u]
  }
}
int main(){
  cin>>n;
  for(int i=1,x,y;i<n;i++){
    cin>>x>>y;
    e[x].emplace_back(y,1);
    e[y].emplace_back(x,1);
  }
  dfs(1,0);
  cout<<md;
}

 

posted @ 2024-09-07 07:06  董晓  阅读(741)  评论(0)    收藏  举报