关于LCA和RMQ问题
一、最近公共祖先(Least Common Ancestors) 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。 这里给出一个LCA的例子: 例一 对于T=<V,E> 则有: LCA(T,5,2)=1 RMQ问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小值下标。这时一个RMQ问题的例子: 例二 对数列:5,8,1,3,6,4,9,5,7 有: RMQ(2,4)=3 RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。 下面给出LCA问题向RMQ问题的转化方法。 对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的深度存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求E[RMQ(D,R[u],R[v])],(R[u]<R[v])。例如,对于第一节的例一,求解步骤如下: 数列E[i]为:1,2,1,3,4,3,5,3,1 于是有: LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1 易知,转化后得到的数列长度为树的结点数的两倍加一,所以转化后的RMQ问题与LCA问题的规模同次 |
数据很水,但是算法绝对经典,题目要求两个节点的最近公共祖先,这个tarjan算法使用了并查集+dfs的操作。这个算法研究了我两个小时,开始的时候在网上查找资料,写的都不尽人意,看不明白,后来干脆直接看代码,模拟了一遍,搞懂了。。。看来计算机的世界,只有代码才是王道!中间的那个并查集操作的作用,只是将已经查找过的节点捆成一个集合然后再指向一个公共的祖先。另外,如果要查询LCA(a,b),必须把(a,b)和(b,a)都加入邻接表。


























































































































不过可能是用了vector的关系,耗时47MS,也许改成单纯的链表要更好些;
还有就是,如果这个树的根节点不确定,边没有方向可言的话,又应该如何做呢?(找不到根了。。。)
如果要找两个节点之间的一条通路 又该怎么办?
看来,这个问题还有继续研究下去的必要。。。
———————————————————————传说中的分割线——————————————————————————
指针版:











































































































































汗,写了个指针版运行速度居然比vector还慢,太打击我对指针的信心了。。。可能是数据太弱了吧,指针的优势没有发挥出来。。。
——————————————————传说中的分割线——————————————————————————————————
终于用RMQ过了,起初一直RE,原来是RM Q里面的数组开小了。。。不过感觉时间效率不是很高,110MS,应该是询问的次数太少的原因吧。
RMQ应付的应该是询问次数非常巨大的情况。而且它是在线的算法,可以按顺序输出结果,这样才能AC题目啊。

















































































































