4月4日,应学弟要求去了次学校给小同学们讲了一堂课,其实讲的挺内容挺杂的,但是目的是引出LCA算法。

现在整理一下当天讲课的主要内容:

开始并没有直接引出LCA问题,而是讲了RMQ(Range Minimum/Maximum Query)问题。

RMQ指的是对于给定的一个数组,每一次询问一个区间[L,R]中数字的最小或最大值,一个很经典的问题。

一开始没有学生知道如何求解此问题。我将问题弱化,每一次询问[1,R]中数字的最值,有学生打出了求一个min/max数组即可,此为正解。

这是一个相当正确的做法,有许多学过线段树的同学在碰到这个弱化的问题时也会不假思索的无脑线段树,将简单的问题复杂化。学生能直接答出min/max数组,我认为也是因为他们现在会的算法还太少,基本没有干扰项。

我接下来介绍了ST表(Sparse Table)求解RMQ。

如果我们现在有一个二维数组f[i][j]表示从第i个数开始包括自己向后总共2^j个数字中的最值,如何求解RMQ?

底下有学生给了一种我觉得挺不错的思路:求出L与R的范围D,将D作二进制分解,则可以在log(D)的复杂度呢,通过若干个f数组中的值求解得出L与R之间数字的最值。

这个思路相当nice,但是我说,其实还是不够优秀,因为事实上只要至多2个数字便可以得出结果了。

如果D是一个2的幂,则只f[L][log2(D)]直接就是答案,否则答案为min/max(f[L][(int)log2(D)][R-2^((int)log2(D))+1][(int)log2(D)]),看上去有些复杂,其实写在代码中比较方便,含义就是求出最大的k使得2^k小于D,接着用左边和右边2段的f值一定能完整覆盖到整个范围区间。

实际上,上述两种情况可以归一化为第二种计算方式。

接下来的问题就是如何求解f数组。其实ST表本质上是一种动态规划的思想,首先初始化所有的f[i][0]为a[i],也即原来的值,接下去从1开始向上循环,直到超过log(n)。f[i][j]=min/max(f[i][j-1],f[i+2^(j-1)][j-1])

至此ST表算法讲授完毕。另外一点想提的是,很多人会把RMQ等同于ST表,这是一种误会。RMQ并不是一种算法,是区间最值问题,本质上是一类问题。RMQ当然也可以用线段树解决。甚至brute force也可以称为RMQ的一种算法。

接下去,我讲了有向图深度优先遍历中的四类边,本文不作展开。

最后,我引出了LCA问题。底下的学生没有人知道不用暴力怎么做。但是有的学生的暴力思路还是有些interesting的,例如先遍历一遍树,当发现第一个点后,在回溯时,标记走过的点的值+1,。再遍历一次树,当发现第二个点后,在回溯时,标记走过的点的值+1,第一次发现有个点值为2,则说明是两个点的最近公共祖先。

我觉得这个思路还是有点意思的,因为比较反常规。

我主要讲了三种求解LCA问题的算法。

第一种是将其归约至RMQ问题。如何规约呢?在遍历到一个节点与回溯到一个节点时,打时间戳,并记录这个时间戳对应的节点。则LCA问题可以规约为对应2个点第一次被遍历到的时间戳之间所有所有时间戳,哪个时间戳对应的节点的深度最小。

听起来有些复杂,其实想想就是那么回事情。

在问到如何将LCA规约至RMQ时,有一个学生意外地联想到了第二种方法,不过他没有完整地想出来。

第二种算法叫树上倍增,与ST表有异曲同工之处,f[i][j]表示节点i的第2^j个祖先。

则求解LCA的算法流程大致是这样的,两个点u和v,不妨假设u的深度大于等于v的深度,则让u向上到两点处于同一个深度。如果两个点相同,则直接返回答案。否则从大到小枚举2的幂,看是否两个点向上那么多个祖先是相同的,如果相同,则认为走过头了,continue掉。否则将其置为新的祖先点。最后2个点再向上走一次,也即他们的父亲节点就是LCA。这其中让u向上走到与v相同高度,用的方法与上文RMQ问题中学生提到的二进制分解是一个思路,即将深度差作二进制分解,向上跳log(d)次。

第三种算法是Tarjan发明的一种基于并查集的离线算法。

所谓离线即先一次性将所有的询问保存,对树做深度优先遍历,每一次遍历完之后,将子树对应的集合合并至当前节点。递归完所有子节点后,遍历所有与当前点相关的询问,如果另外一个点已经被访问过了,则这个询问的答案为另一个点所在集合的代表元。

最后我当时提了一下,没有深入讲,是用树链剖分求解LCA问题。有点杀鸡用宰牛刀的感觉了。

【算法】RMQ LCA 讲课杂记的更多相关文章

  1. poj3728之离线LCA+dp思想/RMQ+LCA(非常好的题目)

    题意很简单 给一个树(n < 5w) 每个点有个权值,代表商品价格 若干个询问(5w) 对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品,   ...

  2. tarjan算法求LCA

    tarjan算法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 这里我们使用tarjan算法离线算法解决这个问题. 离线 ...

  3. 【37.48%】【hdu 2587】How far away ?(3篇文章,3种做法,LCA之ST算法(RMQ))

    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...

  4. 【图论算法】LCA最近公共祖先问题

    LCA模板题https://www.luogu.com.cn/problem/P3379题意理解 对于有根树T的两个结点u.v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深 ...

  5. dfs序 + RMQ = LCA

    dfs序是指你用dfs遍历一棵树时,每个节点会按照遍历到的先后顺序得到一个序号.然后你用这些序号,可以把整个遍历过程表示出来. 如上图所示,则整个遍历过程为1 2 3 2 4 5 4 6 4 2 1 ...

  6. poj 1330(RMQ&LCA入门题)

    传送门:Problem 1330 https://www.cnblogs.com/violet-acmer/p/9686774.html 参考资料: http://dongxicheng.org/st ...

  7. 算法笔记--lca倍增算法

    算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...

  8. hdu 2874 Connections between cities(st&rmq LCA)

    Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  9. hihoCoder#1067(离线算法求LCA)

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中 ...

随机推荐

  1. SQLServer存储过程实现单条件分页

    SQLServer Procedure Pagination_basic: ALTER PROCEDURE [qiancheng].[Pagination_basic] ( ), --name of ...

  2. html to pdf

    C++ Library to Convert HTML to PDF html2pdf PrinceXML 收费 CutePDF Ghostscript PDFDoc VisPDF PDFDoc Sc ...

  3. Lowest Common Ancestor of a Binary Tree leetcode

    Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...

  4. Source Map入门教程

    部署前端之前,开发者通常会对代码进行打包压缩,这样可以减少代码大小,从而有效提高访问速度.然而,压缩代码的报错信息是很难Debug的,因为它的行号和列号已经失真.这时就需要Source Map来还原真 ...

  5. js使用for in遍历时的细节问题

    今天在看别人代码过程中被 "for in"搞得有点晕,所以好好研究了一下,写下来分享给对 for in遍历理解有问题的朋友. 基本格式: for (property in expr ...

  6. 有关html,css,js,less的使用规范

    写前端写久了,规则跟着开发的项目走,突然觉得是不是该总结总结前端的语言使用规则,看到下面这篇还不错,就直接链接过来了哦 http://zhibimo.com/read/Ashu/front-end-s ...

  7. Spring-java代理技术总结

    Spring 中采用JDk的动态代理和CGLib代理技术在运行期间织入增强,所以用户不需要装备特殊的编译器或者类装载器就可以使用AOP功能. 要使用jdk的动态代理,目标类必须实现接口,而CGLib代 ...

  8. 车大棒浅谈jQuery源码(一)

    背景 因为最近辞职找工作,投了许多家公司.结果简历要么石沉大海,一点音讯都没有,要么就是邮件回复说不匹配.后面加了一些QQ群,才发现原来我工作经验年限太少了.现在深圳都是3经验起步,北京据说更加恐怖. ...

  9. java学习笔记 --- java基础语法

    一.java标识符,关键字,保留字 1.标识符 用来增强程序阅读性自定义的名字.类名,变量名,方法名等都可以被称为标识符 标识符的组成: 1.由数字(0-9),字母(a-z,A-Z),下划线(_),美 ...

  10. c#中遍历各种数据集合的方法

    1.遍历枚举类型 补:typeof()方法中只能传具体的类名.类型名称(int32...),不可以是变量名称.类似的方法有GetType(),GteType()方法继承自object,所以c#中任何对 ...