• 浅谈树的直径和树的重心



    title: 树-学习
    date: 2019-08-02 10:03:56
    tags: NULL

    树的直径

    定义

    给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个数值概念,也可代指一条路径

    性质

    1. 直径两端点一定是两个叶子节点
    2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出
    3. 对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为(x,y),用条边将两棵树连接,那么新树的直径一定是u,v,x,y中的两个点
    4. 对于一棵树,如果在一个点的上接一个叶子节点,那么最多会改变直径的一个端点
    5. 若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点

      算法

      求树的直径有两种算法,一种是跑两边搜索,另一种是用树形DP去求(可惜我不会qwq)
      这里我们来介绍用两遍搜索求树的直径的方法.首先,我们从任意一个点出发,找出和ta的距离最远的点u;然后再从u出发,搜索树上距离u最远的节点v.uv之间的路径就是树的直径.
      证明:OI不需要证明……

      代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      #include<iostream>
      #include<cstring>
      #include<cstdio>
      #include<cmath>
      #include<algorithm>
      #define ll long long
      #define INF 0x7fffffff
      #define re register

      using namespace std;

      int read()
      {
      register int x = 0,f = 1;register char ch;
      ch = getchar();
      while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
      while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
      return x * f;
      }

      struct edge{
      int next,to,v;
      }e[100005];

      int n,m,x,y,z,cnt,maxx,j,d[100005],vis[100005],last[100005];

      void add(int x, int y, int z)
      {
      e[++cnt].to = y;
      e[cnt].v = z;
      e[cnt].next = d[x];
      d[x] = cnt;
      }

      void dfs(int k, int l)
      {
      if(l > maxx)
      {
      maxx = l;
      j = k;
      }
      for(re int i = d[k]; i; i = e[i].next)
      if(!vis[e[i].to])
      {
      vis[e[i].to] = 1;
      last[e[i].to] = k;
      dfs(e[i].to,l + e[i].v);
      }
      }

      void work()
      {
      vis[1] = 1;
      dfs(1,0);
      memset(vis,0,sizeof(vis));
      memset(last,0,sizeof(last));
      maxx = 0;
      vis[j] = 1;
      dfs(j,0);
      printf("%d ",maxx);
      while(j != 0)
      {
      printf("%d ",j);
      j = last[j];
      }
      }

      int main()
      {
      n = read();
      m = read();
      for(re int i = 1; i <= m; i++)
      {
      x = read(); y = read(); z = read();
      add(x,y,z);
      add(y,x,z);
      }
      work();
      return 0;
      }

    树的重心

    定义

    树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个 点后最大连通块(一定是树)的结点数最小。

    性质

    1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
    2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
    3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
    4. 一棵树最多有两个重心,且相邻。

      算法

      和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设d(i)表示以i为根的子树的结点的个数。不难发现d(i)=∑d(j)+1,j∈s(i)。s(i)为i结点的所有儿子结点的编号的集合。程序也十分简单:只需要DFS一次,在无根树有根数的同时计算即可,连记忆化都不需要——因为本来就没有重复计算。

      代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      #include<iostream>
      #include<cstring>
      #include<cstdio>
      #include<cmath>
      #include<algorithm>
      #define ll long long
      #define INF 0x7fffffff
      #define re register

      using namespace std;

      int read()
      {
      register int x = 0,f = 1;register char ch;
      ch = getchar();
      while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
      while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
      return x * f;
      }

      struct edge{
      int next,to,v;
      }e[100005];

      int n,m,x,y,z,cnt,maxx,j,d[100005],vis[100005],last[100005];

      void add(int x, int y, int z)
      {
      e[++cnt].to = y;
      e[cnt].v = z;
      e[cnt].next = d[x];
      d[x] = cnt;
      }

      void dfs(int k, int l)
      {
      if(l > maxx)
      {
      maxx = l;
      j = k;
      }
      for(re int i = d[k]; i; i = e[i].next)
      if(!vis[e[i].to])
      {
      vis[e[i].to] = 1;
      last[e[i].to] = k;
      dfs(e[i].to,l + e[i].v);
      }
      }

      void work()
      {
      vis[1] = 1;
      dfs(1,0);
      memset(vis,0,sizeof(vis));
      memset(last,0,sizeof(last));
      maxx = 0;
      vis[j] = 1;
      dfs(j,0);
      printf("%d ",maxx);
      while(j != 0)
      {
      printf("%d ",j);
      j = last[j];
      }
      }

      int main()
      {
      n = read();
      m = read();
      for(re int i = 1; i <= m; i++)
      {
      x = read(); y = read(); z = read();
      add(x,y,z);
      add(y,x,z);
      }
      work();
      return 0;
      }
  • 相关阅读:
    Opencv算法运行时间
    markdown转换为html
    jQuery类名添加click方法
    box-sizing 盒子模型不改变大小
    nodejs 发送get 请求 获取博客园文章列表
    6、Python3中的常用正则表达式
    5、Python3打印函数名之__name__属性
    4、reduce函数工具的使用
    3、Python字符编码区分utf-8和utf-8-sig
    9、QT QLineEdit 密码模式
  • 原文地址:https://www.cnblogs.com/aurorapolaris/p/13502517.html
Copyright © 2020-2023  润新知