in front:

之前写的博客也是在别的平台(一直水博客) 第一次发博客园  也是想换个平台认真写博客记录学习历程 不需要很多人看到  只是用来认真记录每一段光阴的专注

 

暴力做肯定是很暴力的。。。。

dp的思想去考虑树(带权)的直径(其实本质上就是路径问题 最长路径问题)

状态:设d[ x ]表示根为结点 x的子树中根到叶子的最大距离。

跑一遍dfs 记录d[x] 也就是所谓的记忆化搜索(数组记住了状态) 递推写法不知道有无(因为不会哦)

另一种做法是跑两遍dfs(或bfs)  因为可以证明出树的直径的两个端点 从任意点出发最远点一定有其一

 

 add: 

ans = max(ans, d[x] + d[y] + g[x][i].w);
d[x] = max(d[x], d[y] + g[x][i].w);

对这下面两个语句的理解  可能对于初学者来说理解比较困难  为什么要先更新ans 再更新d[x]?

首先树的直径是从根出发到所有叶子结点的一条最长路径次长路径相加得到

先更新ans 再更新d[x] 此时的d[x]是之前遍历过的子树中根结点到叶子结点的最深路径   而d[y]+g[x][i].w是此时的子结点 i 为根的子树的根结点到叶子结点最深路径  

这样用d[x]+d[y]+g[x][i].w不断更新ans就能得到子树的最长路径和次长路径之和了

(由于本人水平有限可能讲的不是很清楚  后期有机会会补充几张图解)

 

 
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;

struct edge {
    int to;
    int w;
};

int v[N];
vector<edge> g[N];//用vector以及一个结构体存带权图
int d[N],ans;

//树形dp
void dp(int x)
{
    v[x] = 1;
    for (int i = 0; i < g[x].size(); i++) {
        int y = g[x][i].to;
        if (v[y]) continue;
        dp(y);
        //语句在递归语句之后 是递归到最深返回时执行
        ans = max(ans, d[x] + d[y] + g[x][i].w);
        d[x] = max(d[x], d[y] + g[x][i].w);
    }
}

void init()
{
    //建图(树)
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n - 1; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        g[u].push_back({ v,w });
        g[v].push_back({ u,w });
    }
}

signed main() {
    init();
    dp(1);
    cout << ans;
}
    
posted on 2022-07-16 11:39  unique_pan  阅读(182)  评论(0编辑  收藏  举报