【算法】RMQ LCA 讲课杂记
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 讲课杂记的更多相关文章
- poj3728之离线LCA+dp思想/RMQ+LCA(非常好的题目)
题意很简单 给一个树(n < 5w) 每个点有个权值,代表商品价格 若干个询问(5w) 对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品, ...
- tarjan算法求LCA
tarjan算法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 这里我们使用tarjan算法离线算法解决这个问题. 离线 ...
- 【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) ...
- 【图论算法】LCA最近公共祖先问题
LCA模板题https://www.luogu.com.cn/problem/P3379题意理解 对于有根树T的两个结点u.v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深 ...
- dfs序 + RMQ = LCA
dfs序是指你用dfs遍历一棵树时,每个节点会按照遍历到的先后顺序得到一个序号.然后你用这些序号,可以把整个遍历过程表示出来. 如上图所示,则整个遍历过程为1 2 3 2 4 5 4 6 4 2 1 ...
- poj 1330(RMQ&LCA入门题)
传送门:Problem 1330 https://www.cnblogs.com/violet-acmer/p/9686774.html 参考资料: http://dongxicheng.org/st ...
- 算法笔记--lca倍增算法
算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...
- hdu 2874 Connections between cities(st&rmq LCA)
Connections between cities Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (J ...
- hihoCoder#1067(离线算法求LCA)
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中 ...
随机推荐
- KoaHub平台基于Node.js开发的Koa EJS渲染插件代码信息详情
koa-ejs ejs render middleware for koa koa-ejs Koa ejs view render middleware. support all feature of ...
- Java Stream API入门篇
本文github地址 你可能还没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少类就清楚了.Java 8之所以费这么大功夫引入函数式编程,原因有二: 代码简洁,函数式编程写 ...
- 解决Gerrit的git unpack error问题和error Missing unknown ec867cebfd2be97c3603c45fac03c75dcf68d0ca
参考链接:http://www.cnblogs.com/yuxc/p/3508964.html 解决方法: 由于帖子里面用的是mysql数据库,而我用的是h2数据库,还特意自己去找了H2数据库的进入方 ...
- 引用类型-----Object
本文章转载至:https://segmentfault.com/a/1190000003984584 关于引用类型的概念: 引用类型:引用类型的值(对象)是引用类型的一个实例: 对象:是某个特定引用类 ...
- Linux文件权限rwx简单了解
Ⅰ 了解Linux下的文件权限 如上图所示,ll命令详细展示当前目录下的文件或者子目录信息 红框标注的即为此文件或者目录的权限 [第一行文件10.c的权限以-开头,用来说明这是一个文件:第四行cod ...
- 谈谈Backbone.js中的el
小编最近开始接触backbone.js,这个曾经非常优秀的一款MVC前端框架,在学习的过程中,遇到下图的这样一个问题 下面上代码 小编的想法很简单,只是view了一个实例,然后在initalize中调 ...
- ubuntu如何进入local、bin目录
回到home目录,输入命令:cd /usr/local 若要进入bin目录,输入命令:cd /usr/local/bin
- 前端学PHP之自定义模板引擎
前面的话 在大多数的项目组中,开发一个Web程序都会出现这样的流程:计划文档提交之后,前端工程师制作了网站的外观模型,然后把它交给后端工程师,它们使用后端代码实现程序逻辑,同时使用外观模型做成基本架构 ...
- js移动端横竖屏检测
方法一:用resize事件来判断,利用屏幕的宽高比,来判断横竖屏 (兼容性较好) (function () { var updateOrientation = function () { var or ...
- Java位向量的实现原理与巧妙应用
Java位向量的巧实现原理与巧妙应用 1.博文介绍 本篇博文将会介绍几本的位运算含义.位向量介绍.BitSet实现原理.Java位向量的应用.拓展介绍Bloom Filter等. 2.位运算介绍 1) ...