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

  • 相关阅读:
    Python 学习笔记 11.模块(Module)
    Python 学习笔记 8.引用(Reference)
    Python 学习笔记 9.函数(Function)
    Python 学习笔记 6.List和Tuple
    Python 学习笔记 4.if 表达式
    Python 学习笔记 2.自省
    Python 学习笔记 3.简单类型
    Python 学习笔记 7.Dictionary
    Python 学习笔记 5.对象驻留
    Python 学习笔记 10.类(Class)
  • 原文地址:https://www.cnblogs.com/PencilWang/p/5925778.html
Copyright © 2011-2022 走看看