LCA 算法总结
LCA
LCA的Tarjan实现
emmmm最近公共祖先的实现方式有很多,比如倍增和Tarjan……我感觉后者写起来是比较简单的,好吧事实上我只会后者,所以就只讲一下Tarjan实现LCA的方式吧。复杂度玄学,大概是点的个数加上每一个点所连出来边的数量吧,其实是很快的。
算法的本质其实就是利用DFS对整个图进行遍历,然后在访问过后记录下来每个节点的上一个节点father[x],如果一对需要求出LCA的点中,一个点已经访问过了,另一个点正在访问,那么访问顺序一定就是从已经访问过的点不断往回退,经过两者的LCA之后向另外一个方向拓展,遇到了正在访问的另一个点,那么因为记录上一个节点的数组father[x]是在回退的过程中记录的,那么因为已经访问过的点肯定回退到了其LCA,否则不经过公共祖先是没有办法访问到另外一个点的。
那么我们就可以通过已经访问过的那个点留下的father[]记录来求出来LCA了,我们只需要不断的往前面的节点走,直到前面的点没有被更新,也就是father[x]=x,这个点就是这一对点的最近公共祖先了。
其实LCA是挺简单的,无论是代码还是理解,其实我觉得我解释的不是很好,所以给上一篇模拟过程的博客吧,用图像来模拟一遍可以加深理解的。
博客链接:LCA 最近公共祖先 : JVxie
- 还有一点,LCA属于离线算法,但是题目要求按照读入的顺序输出的时候,因为没有办法通过二位数组记录下来两个节点x和y的LCA值,我们就需要在建图的时候,把读入的顺序也记录下来,然后求出来LCA之后,用一维数组按照读入时记录下来的的顺序存到对应的位置,这样一遍for循环就可以输出了。
LCA代码:
1 |
|
NOIP2013 货车运输
题目描述:
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入:
- 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
- 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。
- 接下来一行有一个整数 q,表示有 q 辆货车需要运货。
- 接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出:
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
题解:
没有一等奖,肯定要考NOIP啊,写正解这方面,写正解是不可能写正解的,这辈子都不可能写正解的。骗分也不会骗,就是写这种暴力,才能维持到一等分数线的样子。进机房感觉像回家一样,在机房里的感觉比班级里感觉好多了!里面个个都是人才,说话又好听,我超喜欢里面的! ——码·欧埃尔。
其实这道题,emmmm自己又犯了以前的老错误了,唉,明明还写到了个人签名上面防止犯错,结果还是……这道题拿在手里一般都是直接写暴力了,是的考试的时候我是绝对不会写正解的,就算知道正解该怎么写也是不会写的,因为太容易挂掉了。
算了说说正确的解法吧,其实这道题蛮简单的,就是一遍最大生成树加LCA就可以了。首先我们需要解决求最短路最长的问题,我们可以建立一个最大生成树,这样的话里面的边是可选择边的最大值,这样的话可以保证最短路最长。因为最大生成树是通过从大到小排序边,然后再逐个的选择,所以可以保证最短路最长的,因为选择的边一定是可以选择的最长的。
然后我们就可以在最大生成树上进行LCA了,最大生成树的路径之间是唯一的,所以说我们如果求两个点之间的路径,可以通过O(n)求出LCA,然后利用两者的LCA来直接遍历路径,其核心就在于LCA的father[x]数组,这样可以两个个点都通过father[x]来到达其LCA,中间只需要判断一个最小值就可以了,可谓是非常方便的。
最后需要注意的问题就是,最大生成树不一定是联通的,需要访问每一个联通子图!!!还有在通过father[x]来确定两点之间的路径的时候,一定要想到上面的情况是比较特殊的,需要特别的判断!
代码:
1 |
|