zoukankan      html  css  js  c++  java
  • 树的直径

    定义

    我们将一棵树T = ( V,E )的直径定义为maxδ ( u,v ) ( u,v ∈ V ),也就是说,树中所有最短路径距离的最大值即为树的直径。

    寻找方法

    两次bfs

    方法:先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是是的直径

    证明如下:

    ①若P已经在直径上,根据树的直径的定义可知Q也在直径上且为直径的一个端点

    ②若P不在直径上,我们用反证法,假设此时WQ不是直径,AB是直径

    --->若AB与PQ有交点C,由于P到Q最远,那么PC+CQ>PC+CA,所以CQ>CA,易得CQ+CB>CA+CB,即CQ+CB>AB,与AB是直径矛盾,不成立,如下图(其中AB,PQ不一定是直线,画成直线是为了方便):

     --->若AB与PQ没有交点,M为AB上任意一点,N为PQ上任意一点。首先还是NP+NQ>NQ+MN+MB,同时减掉NQ,得NP>MN+MB,易知NP+MN>MB,所以NP+MN+MA>MB+MA,即NP+MN+MA>AB,与AB是直径矛盾,所以这种情况也不成立,如下图:

     树状DP

    对于每个节点我们要记录两个值:

    f1 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的最大值

    f2 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的次大值

    对于一个节点,它到叶子结点距离的最大值和次大致所经过的路径肯定是不一样的

    若j是i的儿子,那么(下面的 w [ i ][ j ] 表示 i 到 j 的路径长度):

    若 f1 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ i ],f1 [ i ] = f1 [ j ] + w [ i ][ j ];否则,若 f2 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ j ] + w [ i ][ j ]; 

    最后的答案 answer = max { f1 [ i ] + f2 [ i ] }

    习题

    https://www.luogu.com.cn/problem/P5536

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n, k,cnt=0,mdeep=-1,mi;
    #define maxn 100010
    struct edge
    {
        int to;
        int next;
    }e[maxn *2];
    int father[maxn],head[maxn],deep[maxn],maxdeep[maxn],dis[maxn];
    bool cmp(int a, int b)
    {
        return a > b;
    }
    void addedge(int u,int v)
    {
        cnt++;
        e[cnt].to = v;
        e[cnt].next = head[u];
        head[u] = cnt;
    }
    void dfs1(int s,int fa)
    {
        if (deep[s] > mdeep)
        {
            mdeep = deep[s];
            mi = s;
        }
    
        for (int i = head[s]; i; i = e[i].next)
        {
            int y = e[i].to;
            if (y == fa)continue;
            deep[y] = deep[s] + 1;
            dfs1(y, s);
        }
    }
    
    void dfs2(int s, int fa)
    {
        if (deep[s] > mdeep)
        {
            mdeep = deep[s];
            mi = s;
        }
    
        for (int i = head[s]; i; i = e[i].next)
        {
            int y = e[i].to;
            if (y == fa)continue;
            father[y] = s;//记录路径
            deep[y] = deep[s] + 1;
            dfs2(y, s);
        }
    }
    void dfsk(int s, int fa)
    {
        maxdeep[s] = deep[s];
        for (int i = head[s]; i; i = e[i].next)
        {
            int y = e[i].to;
            if (y == fa)continue;
            deep[y] = deep[s] + 1;
            dfsk(y, s);
            maxdeep[s] = max(maxdeep[s], maxdeep[y]);//自己的子节点能到达的最深深度,自己也一定能去
            
        }
    }
    int main()
    {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <=n - 1; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            addedge(a, b);
            addedge(b, a);
        }
        dfs1(1, 0);
        memset(deep, 0, sizeof(deep));
        mdeep = -1;
        dfs2(mi, 0);
        int temp = mi;//mi:直径末端上的点
        for (int i = 1; i <= (deep[mi]+1) / 2; i++)temp = father[temp];//之前的记录路径就是为了这里服务的,从直径的末端走该直径长度的一半就是直径的中点
        memset(deep, 0, sizeof(deep));
        dfsk(temp, 0);
        for (int i = 1; i <= n; i++)
            dis[i] = maxdeep[i] - deep[i];
        sort(dis + 1, dis + n + 1, cmp);
        int ans = 0;
        for (int i = k + 1; i <= n; i++)
            ans = max(ans, dis[i] + 1);
        printf("%d
    ", ans);
    }

    参考:https://blog.csdn.net/forever_dreams/article/details/81051578

  • 相关阅读:
    mv命令(转)
    Linux获得命令帮助(学习笔记五)
    Shell解释器(学习笔记四)
    rmdir 命令(转)
    Java从零开始学十八(抽象类和接口)
    rm 命令(转)
    Centos6.6系统root用户密码恢复案例(转)
    Java从零开始学十七(简单工厂)
    Java从零开始学十六(多态)
    mkdir命令(转)
  • 原文地址:https://www.cnblogs.com/Jason66661010/p/13168844.html
Copyright © 2011-2022 走看看