树形dp

树上任意两点都是连通的,相互可达的。

找树的直径:

(一)DFS || BFS,(树上最远的两个点之间的距离,相邻两个点之间距离为1)

1. 任取一点作为起点,找到距离该点最远的一个点 u。u 一定是某条直径的一个端点。DFS || BFS(BFS更优,防止爆栈)

2. 再找到距离 u 最远的一点 v。DFS || BFS(BFS更优,防止爆栈)

那么 u 和 v 之间的路径就是一条直径。

 

(二)从树形dp角度看,使用DFS,(边权为正负均可)

我们把每一个点归为一类,然后将每条路径归到它所经过的最高的那个点的这一类里。于是,我们可以按点将所有的路径分类。然后我们枚举每个点,对于每个点,再枚举挂在该点上的所有路径即可,求出这些路径长度的最大值。对于每个点求最大值的过程中,先求出每个子节点向下走的最大长度。可以分为两类:一类是路径以该点作为端点,那么就是求从这个点向下走最远能走多少。此时枚举所有子节点能向下走的最大距离,再各自加上从该点到子节点这条边的长度,从中取最大值即可。第二类是该点为路径中的某一点(非端点),路径穿过这个点,那么我们就求从该点向下能够走的最长路径和次长路径之和。给一堆数,求最大值和次大值,只需遍历一遍,不需要排序。如果当前数大于最大数,我们就把最大数赋给次大数,然后更新最大数,否则,我们再判断当前数如果大于次大数,那么我们更新次大数。

https://www.acwing.com/problem/content/1074/

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 7; //由于边有正反两个方向,所以扩大一倍
int n, cnt, ans;
int head[maxn];
struct edge
{
    int from, to, next, w;
}e[maxn];

void add(int u, int v, int w)
{
    e[++cnt].next = head[u];
    e[cnt].from = u;
    e[cnt].to = v;
    e[cnt].w = w;
    head[u] = cnt;
}

int dfs(int u, int father)
{
    int dist = 0;
    int d1 = 0, d2 = 0;
    for(int i = head[u]; i; i = e[i].next)
    {
        int j = e[i].to;
        if(j == father) continue;
        int d = dfs(j, u) + e[i].w;
        dist = max(dist, d);
        if(d >= d1) d2 = d1, d1 = d;
        else if(d > d2) d2 = d;
    }
    ans = max(ans, d1 + d2);
    return dist;
}


int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n - 1; ++i)
    {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        add(u, v, w), add(v, u, w);
    }
    dfs(1, -1);
    printf("%d\n", ans);
}

 

 

(三)

posted @ 2020-12-21 13:32  .Ivorelectra  阅读(103)  评论(0编辑  收藏  举报