【学习笔记】图论 Tarjan LCA 割点 桥 暑假7.5

来源:互联网 发布:sql delete 删除列 编辑:程序博客网 时间:2024/06/06 12:57

简介

Tarjan作为一位算法大师,发明了许多算法。本篇博文介绍一下Tarjan框架下的求解树上LCA(最近公共祖先)的离线算法,复杂度O(N+Q)。以及求割点,桥的算法,复杂度O(V+E),即dfs 1次所需的时间。

Tarjan_LCA

Problem:

一棵树,n个节点,Q组询问(x,y),求x,y的LCA

思路:

  1. 最暴力的想法,x,y都向上搜索,因为向上的节点就是x或y的祖先,所以搜到的第一个公共节点就是x和y的LCA,复杂度O(QN);

  2. 一种简单的优化方法是把关于x的所有询问一起处理,这样x只需要向上搜索1次,优化一下常数;

  3. 任何题目的方法归根结底都是枚举,我们注意到之前我们是在枚举询问,规模为O(Q),一般Q是O(N^2)级别的,所以我们似乎可以换一下思路,去枚举一下答案,也就是LCA,这个的规模只是O(N)。——————————在观察我们发现u的不同子树中的节点的LCA是u,这样,一个想法就形成了。现在让我们来看一下算法导论中完整版的dfs:

//CLRS 中文版 p350 dfs模板;dfs(G){    for each vertex u in G.v        u.color=WHITE        u.pre=NIL    time=0    for each vertex u in G.v        if u.color==WHITE            dfs_visit(G,u);}dfs_visit(G,u){    time=time+1    u.d=time    u.color=GRAY    for each v in G:Adj[u]        if v.color==WHITE            v.pre=u            dfs_visit(G,v)    u.color=BLACK    time=time+1    u.f=time}

请读者仔细阅读这段伪代码,这几乎是在全面的前提下最精简的版本了,少了任何一句话都不行,尤其是其中的“白”“灰”“黑”三种颜色与时间戳的概念,在很多高级算法中是必不可少的,也是很多高级算法创造根源,Tarjan_LCA算法在这个基础上就很容易理解。

这里写图片描述

来个例子,本例子中,x1 < x2 < x3 < x4 < x5 < x6
画出时间戳可以发现u,v的时间戳不相交,这样我们可以轻易得出以下定理:

  • 当出现“灰黑灰”时,x为u向上第一个灰点,则path(u,x)上的点和v的LCA为x。
    简单来说就是上面的直觉,x下一颗子树u内节点与另外子树中节点的LCA均为x
    这样的性质让我们想到了一种数据结构——并查集
    废话不多说,上CLRS伪代码:
LCA(u)    MAKE_SET(u);    FIND_SET(u).ancestor=u;    for each child v of u in T        LCA(v)        UNION(u,v)        FIND_SET(u).ancestor=u    u.color=BLACK    for each node v such that (u,v) in P        if v.color==BLACK            print "The least common ancestor of"             u "and" v "is" FIND_SET(v).ancestor

http://www.cnblogs.com/JVxie/p/4854719.html
这个网站有比较详细的模拟过程,可以看一看加深理解
http://blog.csdn.net/jarily/article/details/8947928
再转一份比较清晰的解释:
*算法思想:
*Tarjan_LCA离线算法;
*Tarjan算法基于dfs的框架,对于新搜到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每个子树进行搜索;
*每搜索完一棵子树,则可确定子树内的LCA询问都已解决,其他的LCA询问的结果必然在这个子树之外;
*这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先;
*之后继续搜索下一棵子树,直到当前结点的所有子树搜完;
*
*这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问;
*如果有一个从当前结点到结点v的询问,且v已经被检查过;
*则由于进行的是dfs,当前结点与v的最近公共祖先一定还没有被检查;
*而这个最近公共祖先的包含v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先;
*
*算法步骤:
*对于每一个结点:
*(1)建立以u为代表元素的集合;
*(2)遍历与u相连的结点v,如果没有被访问过,对于v使用Tarjan_LCA算法,结束后将v的集合并入u的集合;
*(3)对于与u有关的询问(u,v),如果v被访问过,则结果就是v所在集合的代表元素;

习题:
模板题:
1.poj 1330
2.http://acm.zjnu.edu.cn/CLanguage/showproblem?problem_id=1232 交通运输线

原创粉丝点击