树的直径小结

树中最远的两个节点之间的距离被称为树的直径,链接这两个点的链为最长链(也称直径)

树形dp求树的直径

  考虑求经过结点x的最长链的长度f[x],那么直径就是max{f[x]}

  求f[x]数组:经过x结点的最长链的长度由四个部分组成:儿子yi的最大深度,(x,yi),儿子yj的最大深度,(x,yj)

  显然经过x的最长链要么是以x为端点,要么将x点包含在链内,当以x为端点时f[x]就是x的最大深度,当将x包含在链内时f[x]就是x往下走的深度最大和次大的值

  dfs时先求以x为端点的情况,再求包含x的情况

  用dp[x]表示x的最大深度,dp时先求出x的儿子y的最大深度dp[y],那么f[x]=max(f[x],d[x]+d[y]+edge[i].to);

              然后再更新x的最大深度dp[x]

void dfs(int x){
    v[x]=1;
    for(int i=head[x];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(v[y])continue;
        dfs(y);
        ans=max(ans,dp[x]+dp[y]+edge[i].w);
        dp[x]=max(dp[x],dp[y]+edge[i].w);
    }
}

cf1084d,树的直径变形

/*
给定n城市,m条道路,每条路耗油w,每个点有油a[i],从任意点出发,求最大可以剩下的油 
dp[i]表示从i开始往下走的最大收益,ans表示最大结果 
因为走过的路不能走,所以可以想到最优解肯定经过某个点u,其余点都是其子节点
并且即使有分叉,也一定在这个点u上
那么在dp时先处理好子节点,获得所有的dp[son],然后再更新dp[u]和ans即可 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 300005
#define ll long long
struct Edge{int to,nxt,w;}edge[maxn<<1];
int n,m,a[maxn],head[maxn],tot;
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
void addedge(int u,int v,int w){
    edge[tot].w=w;
    edge[tot].to=v,edge[tot].nxt=head[u],head[u]=tot++;
}
ll dp[maxn],ans;//
void dfs(int u,int pre){
    dp[u]=a[u];ans=max(dp[u],ans);
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==pre)continue;
        dfs(v,u);
        ans=max(ans,dp[u]+dp[v]-edge[i].w);//前面的是dp[u]而不是a[i]表示可以承受一条岔路 
        dp[u]=max(dp[u],a[u]+dp[v]-edge[i].w);//根据dp[u]定义即可 
    }
} 
int main(){
    init();
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<n;i++){
        int u,v,w;
        cin>>u>>v>>w;
        addedge(v,u,w);
        addedge(u,v,w);
    }
    dfs(1,0);
    cout<<ans<<endl; 
}

另外通过两次dfs/bfs求树的直径也可以

原文地址:https://www.cnblogs.com/zsben991126/p/10506146.html