zoukankan      html  css  js  c++  java
  • 树的直径的两种求法

    打多校才发现连树的直径都忘了,赶紧来复习一下树的直径
    这里只讲裸的树的直径..
    有两种常见的方法去求树的直径

    树形dp写法:

    第一种是树形dp,我们随便选个结点作为根将有根树转化为无根树,用dp[x]表示以x为根的子树中从x出发的最长链的长度,如果Yi是x的儿子们,那么状态转移是这样的dp[x]=max(dp[x],dp[y]+Edge),接下来考虑经过x的最长链的长度f[x],那么max(f[i],1<=i<=n)就是树的直径了,假设yi,yj是x的两个直接相连的结点,那么f(x)=max(dp[yi]+dp[yj]+x与yi,yj的距离)

    具体实现就在下面了

    #include <bits/stdc++.h>
    using namespace std;
    #define de(a) cout << #a << " = " << a << endl
    #define rep(i, a, n) for (int i = a; i < n; i++)
    #define per(i, a, n) for (int i = n - 1; i >= a; i--)
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> PII;
    typedef pair<double, double> PDD;
    typedef vector<int, int> VII;
    #define inf 0x3f3f3f3f
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const ll MAXN = 1e3 + 7;
    const ll MAXM = 1e5 + 7;
    const ll MOD = 1e9 + 7;
    const double eps = 1e-6;
    const double pi = acos(-1.0);
    int n, m;
    struct Edge
    {
        int from, to, val;
        int next;
    } E[MAXM];
    int cnt = -1;
    int ans = -inf;
    int head[MAXN];
    int dp[MAXN]; /* dp[x]表示从结点x出发,往以x作为根的子树走,能够到达的最远距离 */
    void add(int from, int to, int val)
    {
        E[++cnt].from = from;
        E[cnt].to = to;
        E[cnt].val = val;
        E[cnt].next = head[from];
        head[from] = cnt;
    }
    void init()
    {
        ans = -inf;
        cnt = -1;
        memset(head, -1, sizeof(head));
        memset(dp, 0, sizeof(dp));
    }
    /* 求树的直径 */
    void tree_dp(int u, int fa) /* 随便选个结点作为根结点把树转换成有根树 */
    {
        for (int i = head[u]; i != -1; i = E[i].next)
        {
            int to = E[i].to;
            if (fa == to)
                continue;
            tree_dp(to, u);
            ans = max(ans, dp[u] + dp[to] + E[i].val);
            dp[u] = max(dp[u], dp[to] + E[i].val);
        }
    }
    int main()
    {
        init();
        cin >> n >> m;
        for (int i = 0; i < m; i++)
        {
            int u, v, val;
            cin >> u >> v >> val;
            add(u, v, val); //建边
            add(v, u, val);
        }
        tree_dp(1, -1);
        cout << ans << endl;
        return 0;
    }
    

    两次dfs或者bfs:

    这样写还方便计算出直径上的具体结点。
    第一次dfs从任意结点出发对树进行遍历,求出与出发点距离最远的结点记为x
    那么从x出发,通过第二次dfs在进行一次遍历,求出与x距离最远的结点y,该结点与结点x得路径长度就是树的直径,这个方法是显然正确的,x肯定是直径的一端,y就是另一端,相连就是树的直径
    bfs也差不多...但是不是很想写...

    两次dfs具体实现

    #include <bits/stdc++.h>
    using namespace std;
    #define de(a) cout << #a << " = " << a << endl
    #define rep(i, a, n) for (int i = a; i < n; i++)
    #define per(i, a, n) for (int i = n - 1; i >= a; i--)
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> PII;
    typedef pair<double, double> PDD;
    typedef vector<int, int> VII;
    #define inf 0x3f3f3f3f
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const ll MAXN = 1e3 + 7;
    const ll MAXM = 1e5 + 7;
    const ll MOD = 1e9 + 7;
    const double eps = 1e-6;
    const double pi = acos(-1.0);
    int n, m;
    struct Edge
    {
        int from, to, val;
        int next;
    } E[MAXM];
    int cnt = -1;
    int ans = -inf;
    int head[MAXN];
    int vis[MAXN];
    int dis[MAXN];
    void add(int from, int to, int val)
    {
        E[++cnt].from = from;
        E[cnt].to = to;
        E[cnt].val = val;
        E[cnt].next = head[from];
        head[from] = cnt;
    }
    void init()
    {
        ans = -inf;
        cnt = -1;
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(dis, 0, sizeof(dis));
    }
    /* 求树的直径 */
    void dfs(int u)
    {
        vis[u] = 1;
        for (int i = head[u]; i != -1; i = E[i].next)
        {
            int to = E[i].to;
            if (vis[to])
                continue;
            dis[to] = dis[u] + E[i].val;
            dfs(to);
        }
        return;
    }
    int main()
    {
        init();
        cin >> n >> m;
        for (int i = 0; i < m; i++)
        {
            int u, v, val;
            cin >> u >> v >> val;
            add(u, v, val); //建边
            add(v, u, val);
        }
        dfs(1); /* 第一遍dfs */
        int maxx = -inf, start = -1;
        for (int i = 1; i <= n; i++)
        {
            if (dis[i] > maxx)
            {
                maxx = dis[i];
                start = i;
            }
            dis[i] = vis[i] = 0;
        }
        dfs(start); /* 第二遍dfs */
        for (int i = 1; i <= n; i++)
            ans = max(dis[i], ans);
        cout << ans << endl;
        return 0;
    }
    /* 7 6
    1 2 1
    1 3 4
    2 4 2
    2 5 3
    3 6 5
    3 7 6 */
    
  • 相关阅读:
    luogu P1382 楼房
    luogu P1908 逆序对
    5.28 模拟赛
    POJ 2991 Crane
    残(矩阵快速幂)
    AC日记——拍照 洛谷 P3410
    AC日记——[CQOI2014]危桥 洛谷 P3163
    AC日记——【模板】二分图匹配 洛谷 P3386
    AC日记——[ZJOI2009]假期的宿舍 cogs 1333
    AC日记——城市 洛谷 P1401
  • 原文地址:https://www.cnblogs.com/graytido/p/11258010.html
Copyright © 2011-2022 走看看