zoukankan      html  css  js  c++  java
  • 求解LCA问题的几种方式

    求解LCA问题的几种方式

    这篇随笔讲解图论中LCA问题(最近公共祖先)的几种求解方式及实现方法。LCA问题属于高级图论,所以希望读者学习过初级图论,知道图的一些基本知识,并懂得深搜算法的实现方式。这样理解本篇博客将会快捷、舒适。

    知识准备

    理解LCA问题,理解节点深度是至关重要的,大家可以画一棵树。在一棵树中,所有的节点都有一个深度。根节点的深度是1,其他节点的深度可以用深搜遍历树来处理出来。这样,我们就可以通过深度数组来实现解决LCA问题的算法。

    朴素LCA算法

    朴素LCA算法的实现过程大约是这样:对于询问的两个点(x,y),先判断两个点谁更深一些,然后把更深的点顺着它的父节点一步步往上提升,直到和(y)点的深度相等为止。然后同时提升(x,y)两点,直到这两个点变成同一个点,这时的那个点就是我们要求的LCA。

    根据这个算法实现的特点,我们叫他“爬一爬”算法。这种算法极容易理解,但是奇慢无比。所以我们就不给代码了。

    倍增LCA算法

    刚刚提到的LCA朴素算法比较好理解,但是奇慢无比。所以我们推出了更高级一点的方法:倍增LCA。倍增思想其实是非常好用的一种优化思想,在算法优化中有很多使用实例。比如RMQ问题的暴力方法用倍增优化之后就变成了ST表(ST算法),比如LCA的爬一爬算法用倍增优化之后就变成了倍增LCA算法。

    所谓倍增LCA,其实很好理解,就是原来的朴素算法是一个一个爬,我们现在变成一次爬(2^k)个,这样就会大大优化复杂度。

    实现的步骤并没有任何变化:都是先爬比较小的那个,后一起爬。但是这个时候我们就要处理一个二维数组f。(f[x][k])表示(x)的第(2^k)辈的祖先是谁。这样,我们就可以得出一个递推式:

    [f[x][k]=f[f[x][k-1]][k-1] ]

    需要说明的是,这里需要提前处理出f数组和deep数组,对于这种操作,我们可以在树上进行深搜来实现。

    模板如下:

    void dfs(int x,int f)
    {
        deep[x]=deep[f]+1;
        fa[x][0]=f;
        for(int i=1;(1<<i)<=deep[x];i++)
            fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==f)
                continue;
            dfs(y,x);
        }
    }
    

    应该是很好理解的:(x)的第(2^k)辈的祖先就等于(x)的第(2^{k-1})辈的祖先的第(2^{k-1})辈的祖先。

    (其实这是个动态规划的过程)

    剩下的就是一些细节问题。请大家多多注意,以后求LCA问题的时候根据不同的题目要求,就改这个模板即可。

    代码:

    int lca(int x,int y)
    {
    	int ret;
    	if(deep[x]<deep[y])
    		swap(x,y);
    	for(int i=20;i>=0;i--)
    		if(deep[fa[x][i]]>=deep[y])
    			x=fa[x][i];
    	if(x==y)
    		return y;
    	for(int i=20;i>=0;i--)
    	{
    		if(fa[x][i]!=fa[y][i])
    		{
    			x=fa[x][i];
    			y=fa[y][i];
    		}
    		else
    			ret=fa[x][i];
    	}
    	return ret;
    }
    
  • 相关阅读:
    C# 数组
    一个遍历算法
    php csv导出
    linux 配置 crontab
    LINUX 配置SVN
    Linux chkconfig命令
    Linux 安装mysql+apache+php
    linux 安装samba
    linux安装软件的学习
    LINUX中简单的字符命令
  • 原文地址:https://www.cnblogs.com/fusiwei/p/11494132.html
Copyright © 2011-2022 走看看