zoukankan      html  css  js  c++  java
  • 算法描述》LCA两三事(蒟蒻向)

      LCA是图论中常用的解决树形结构子问题的工具,这一问题一般需要用一个简短的子函数直接解决,但是这对于广大蒟蒻们仍然是一个不小的问题。

      LCA是指在树形结构中两点的最近公共祖先,对于这个问题,直接向上找事最直接的方法,但同时时间复杂度和数据给出的生成树的层数有关,最优情况是logN级别的,但是如果数据给出的是一条链就GG了,所以要用更优的方法写,一般来说,用的是log2N的操作,最糟糕的复杂度也是logN级别的,那如何实现这一过程捏,我这里有两种方法,和大家分享

      第一种:树上倍增

      具体方法是对于已经预处理好的f[i][j]数组来实现这一过程,预处理具体过程如下

    1 for(int j=1;j<=n;++j)
    2     f[0][j]=father[j];
    3 for(int i=1;i<=20;++i)
    4     for(int j=1;j<=n;++j)
    5         f[i][j]=f[i-1][f[i-1][j]];

      这段代码需要稍加理解,f[i][j]表示第j个点的2的 i次方的父亲节点。因为一般数据给的不会特别大,所以在 i 的那重循环里,只要到20就够了,因为这是指数级的操作。

      这个预处理是常数级的O(N),只不过常数比较大。

      那么预处理结束后,我们接下来的操作就应该是对于每一组要求LCA的两个数,直接扔进子程序判断

      子程序如下

      

     1 int LCA(int x,int y)
     2 {
     3     if(dep[x]>dep[y])swap(x,y);
     4     for(int i=20;~i;--i)
     5     if(dep[f[i][y]]>dep[x])y=f[i][y];
     6     if(y==x)return x;
     7     for(int i=20;~i;--i)
     8     if(f[i][y]!=f[i][x])y=f[i][y],x=f[i][x];
     9     return f[0][x];
    10 }

       这个子程序很简洁明了,只要会二进制拆分就很好理解,在第3行到第6行写的是输入的两点属于祖先与子孙关系,这时我们只要找一边的祖先节点就好啦;如果这两点属于不同子树,那就缩为同一深度。

      第7到第9行,代表两节点属于两颗子树这时只要不停的趋近就好了,因为我们的判断条件,所以在最后必须输出一个缩后点的父亲节点。

      第二种:欧拉序列

      这是一种更优的写法,主要思路是先将一颗树按欧拉序列处理,然后在每次对欧拉序列中这两点之间的点做一遍RMQ,RMQ的关键字是最小深度。

      但是这种写法较为复杂(我懒),所以代码没有给出,但是只要会RMQ的话这应该就是个较为简单的问题了。

      算法效率分析

      这种方法无论从时间复杂度还是空间复杂度来说都比树上倍增优,但事实上由于树上倍增极小的常数,所以这两个算法在时间复杂度上是相似的,但是在空间上来说欧拉序列更优,从另一个方面来说,树上倍增不仅可以求LCA,还可以顺便求一个路径最大权边之类的问题,并且编程复杂度优,所以一般来说树上倍增是比较常用的。

      有一道半模板题

      BZOJ3732

  • 相关阅读:
    java集合部分原材料
    java--Collection继承结构图
    java--类与类之间的关系
    java--BigDecimal类
    java--生成随机数
    java--枚举类型
    java--数字格式化
    Java--包装类型
    Java--StringBuffer和StringBuilder
    Java--String
  • 原文地址:https://www.cnblogs.com/PencilWang/p/5925778.html
Copyright © 2011-2022 走看看