初级倍增算法(模拟+讲解)

倍增,顾名思义就是一倍一倍的增加。举个例子你每次可以跳2的k次方步。现在你要跳到距离7步的地方。

跳8步 跳过了(不跳)

跳4步 没跳到(跳)

跳2步 没跳到 (跳)

跳1步 跳到 (停)

这里就只跳了3次 比普通的7次跳发优化了4次;

如果数据很大就是O(n) 和 O(logn)的区别了;

这里主要讲RMQ和LCA的2种简单算法

RMQ(区间最值查询)是一个预处理O(n*logn)区间极值O(1)查询任意区间极值的工具

主要思想就是区间dp出每个点起2的k次方的长度内的极值。运用大区间的极值由小区间得到,同时大区间的答案可以由小区间随意组合得到。比如我们已经预处理1为起点长度为4的答案,和2为起点向后4的答案,我们查询区间1到5的极值就可以比较1-4区间和2-5区间的答案来得到1-5的答案;

预处理的代码如下

f数组的意思是以i为起点2的j次方长度的区间的极值是多少

void ST(int n)///预处理
{
    for(int i=1;i<=n;i++)
        f[i][0]=a[i];///初始化以自己为起点2的0次方长的区间就是自己
    for(int j=1;(1<

模拟一下:我们要得到f[1][1]就可以通过f[1][0]和f[2][0]得到 f[1][2]可以由f[1][1]和f[3][1]得到。由于我们是小区间得到大区间,所以比如求区间大小为8的时候,我们已经处理了所以4大小的区间了

查询极值如下

int RMQ(int l,int r)
{
       int k=trunc(log2( r-l+1 ));///向下取整
       printf("k=%d\n",k);

       return max(  f[l][k],  f[r-(1<

代码处的k是找2个小一点的区间可以覆盖玩需要查询的区间,比如查找5长度的区间,我们就找2个长度为4的区间完全覆盖查询的区间。为什么能这样,举个例子1 2 4可以任意组合出1-7之内的所有数,正是这个性质才使得这个算法可以存在。

LCA(最近公共祖先)也可以认为在树上找最短路,因为找到了最近公共祖先也就等于找到了最短路,维护一个dis[]数组表示所有节点到根节点的距离,u到v的最短距离就是dis[u]+dis[v]-2dis[LCA(u,v)];

这个算法利用的是思想是找到u和v第一个不同祖先不同的位置,然后这个位置向上走一步就是最近公共的祖先。

但是u和v不在同一个深度肯定不行,所有需要dfs求去所有节点的深度

需要注意的是根据不同情况DFS是必须找到这个树的根节点,从根节点开始搜索

怎么找到根节点就开自己开标记数组处理啦!

void DFS(int u)
{
    for(int i=0;i

然后要做的就是倍增预处理每个节点上面2的k次方个祖先节点是什么

void ST(int n)///倍增预处理i 节点上面1<

需要注意的是输入边的时候就把f[i][0]初始化好,f[i][0]表示i节点上面2的0次方的祖先是什么,输入的时候很容易就初始化好了。

举个例子

初级倍增算法(模拟+讲解)_第1张图片


f[1][0]=0表示没有祖先,这个1节点就是根
f[2][0]=1表示2号节点祖先是1
以此内推f[4][0]=2  f[5][0]=2  f[3][0]=1
处理f[4][1]的时候就可以通过  f[ f[4][0]] [ 0 ]得到
f[4][0]=2 所以 f[4][1]=f[2][0]就意味着  i的上面2个节点可以通过i的上面1个节点的再跳1个节点得到 同理4个节点可以跳2次2个节点得到.

做完准备工作后就可以很高效的查询任意2个点的公共祖先了

int LCA(int u,int v)
{
    if(deep[u]=0;i--)///找到第一个不相同的节点
    {
        if( f[u][i]!=f[v][i] )///还是利用了 1 2 4 可以任意组合出1-7所有的步数
        {
            u=f[u][i];
            v=f[v][i];
        }
    }
    return f[u][0];///第一个不相同的节点的上一个就是最近公共祖先
}

 

你可能感兴趣的:(动态规划)