树形DP—树的直径

树的直径
通俗的讲就是求树上的最长链的长度
一般有两种求法,各有优势


DP法

d p [ u ] 表示从结点u出发能走到的最远距离
v i 为u的子节点
那么有 d p [ u ] = m a x ( d p [ v i ] + d i s ( u , v ) )
对于经过结点u的最长链长度 m x l e n [ u ]
m x l e n [ u ] = m a x ( d p [ v i ] + d p [ v j ] + d i s ( v i , u ) + d i s ( u , v j ) )
那么整个树的最长链就是 m a x ( m x l e n [ u i ] ) 1 <= u <= n

整个算法只需一次dfs,时间复杂度 O ( n )

void DP(int u,int pa)
{
    dp[u]=0;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        DP(v,u);
        mxlen=max(mxlen,dp[u]+dp[v]+E[i].dis);
        dp[u]=max(dp[u],dp[v]+E[i].dis);
    }
}

DFS || BFS 法

算法总共两次dfs或bfs

第一步
从任意节点出发,通过dfs或bfs遍历找到与出发点最远的结点,记为p

第二步
从p出发,通过dfs或bfs遍历找到离p最远的结点,记为q

此时p到q的路径就是树的直径

该算法整体复杂度为 O ( n 2 )
但是可以同时得知直径的具体结点,以便于其他操作

void dfs(int u,int pa)
{
    //dp[u]记录从u出发能到达的最远距离,rem[u]记录离u最远的结点
    rem[u]=u; dp[u]=0;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        dfs(v,u);
        if(dp[u]<=dp[v]+E[i].dis)
        dp[u]=dp[v]+E[i].dis,rem[u]=rem[v];
    }
}

dfs1(1,0); p=rem[1];
dfs1(p,0); q=rem[p];

需要特别注意的是

当树中有负边权时不能使用该方法

猜你喜欢

转载自blog.csdn.net/niiick/article/details/80708655