超级详细 倍增法 实现 LCA

描述:倍增法用于很多算法当中,通过字面意思来理解就是翻倍增加嘛,这里着重讲使用倍增法在树中的应用求LCA;LCA是啥呢 在一棵树当中 lca表示的是两个节点最近公共祖先, 大家看这课树哈节点5,3的lca就是1,13和11的LCA就是6。节点8,12的lca就是8,那么我们如何通过被增来实现LCA呢。首先大家看下这个数组grand[x][i],这个数组表示标号为x节

大家好,又见面了,我是你们的朋友全栈君。

描述:倍增法用于很多算法当中,通过字面意思来理解就是翻倍增加嘛,这里着重讲使用倍增法在树中的应用求LCA;

超级详细 倍增法 实现 LCA

LCA是啥呢  在一棵树当中 哭lca表示的是两个节点最近公共祖先, 大家看这课树哈节点5 ,3的lca就是1,13和11的LCA就是6。节点8,12的lca就是8,那么我们如何通过被增来实现LCA呢。

首先请大家认真看下面的分析。

depth[x],表示x节点的深度。

大家看下这个数组 grand[x][i] ,这个数组表示标号为x节点向上跳2^i步的节点。例如grand[5][0]=2,因为5这个节点向上跳2^0次方个就是2,当然所有节点向上跳一个就是他的父亲节点,

得到grand[x][0]=他的爸爸吐舌头

那grand[x][1]=什么呢?等于grand[grand[x][0]][0];

既grand[x][i]=grand[grand[x][i-1]][i-1],为什么呢,2^4=2^3+2^3,表示 x向上跳2的i次的节点=x向上跳i-1次方的这个节点向上跳i-1次方,有点绕口。例如 grand[13][2]=grand[6][2]=g[g[rand][i-1]][i-1];

有了这个预处理那我们是不是可以得到每一个节点向上跳i格呢,然后就是i的取值范围为1<=i<=log2(n),向下取整n表示节点个数,也就是说grand[x][log2(n)]就至少可以跳到根节点甚至上面,当然上面啥都没有。

我们这里还有一个数组 gw[x][i],表示x节点向上跳2^i次方的距离(权)。同样的有gw[x][i]=gw[x][i-1]+gw[grand[x][i-1]][i-1],这个公式表示x到他向上跳2的i次方那个节点的距离=x跳2^i-1次方的距离加上,他向上跳2^i-1次方这个点到他上面跳2^i-1次方的距离。

例如1,3,6,10,13,他们之间的边的权等于 2 4 3 6,既gw[13][0]=他爸爸10到他的距离等于6,gw[]13[2]=gw[13][1]+gw[6][1];。

其实我们可以自己加很多类似的数组

例如max[x][i]啊表示 x向上跳2^i次方的边的最大的那条边。这里就不一一举例子子了

现在有了这些预处理。我下面是用dfs实现的预处理;

就看如何实现LCA了吧,

首先 给定两个节点。假如给定的是a=13,b=12.。要我们求他们的lca ,首先第一步 我们要把他们调整到同一深度,既把a向上跳一个步这样他们的深度就相同的了。我们是把深度高的向上调。调整跟深度低的一样。

然后就两个节点一起向上跳一个2^j,首先我们j从log2(n)=3开始跳,跳到0。我们要跳到他们lca的下面两个节点。既3和4,既可以跳的节点是他们不相同并且不超出根节点(grand[a][j]!=grand[b][j]),这里是j=1的时候跳了,j=1因为(grand[a][j]!=grand[b][j]);就执行(a=grand[a][j],b=grand[b][j])a就跳到了3,b就跳到了4,其余的j就不可以跳因为j=3,2就到根的上面了j=0他们就想等了。跳到那,最后退出,grand[a][0]就是他们的lca了。

这就是程序的执行过程。下面看代码

模板体 :hdu2586

代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<stdlib.h>
#include<math.h>
#define max 40001
#define maxl 25
using namespace std;
typedef struct
{
    int from,to,w;
}edge;//这个结构体用来存储边

vector<edge>edges;
vector<int> G[max];
//保存边的数组
int grand[max][maxl],gw[max][maxl];//x向上跳2^i次方的节点,x到他上面祖先2^i次方的距离
int depth[max];//深度
int n,m,N;
void addedge(int x,int y,int w)//把边保存起来的函数
{
   edge a={x,y,w},b={y,x,w};
   edges.push_back(a);
   edges.push_back(b);

   G[x].push_back(edges.size()-2);
   G[y].push_back(edges.size()-1);
}
void dfs(int x)//dfs建图
{
    for(int i=1;i<=N;i++)//第一个几点就全部都是0咯,第二个节点就有变化了,不理解的话建议复制代码输出下这些数组

    {
        grand[x][i]=grand[grand[x][i-1]][i-1];
        gw[x][i]=gw[x][i-1]+gw[grand[x][i-1]][i-1];
       // if(grand[x][i]==0) break;
    }

    for(int i=0;i<G[x].size();i++)
    {   edge  e = edges[G[x][i]];
         if(e.to!=grand[x][0])//这里我们保存的是双向边所以与他相连的边不是他父亲就是他儿子父亲的话就不能执行,不然就死循环了。

           {
                depth[e.to]=depth[x]+1;//他儿子的深度等于他爸爸的加1
                grand[e.to][0]=x;//与x相连那个节点的父亲等于x
                gw[e.to][0]=e.w;//与x相连那个节点的距离等于这条边的距离
                dfs(e.to);//深搜往下面建
           }
    }
}
void Init(){
    //n为节点个数
    N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先
    depth[1]=0;//根结点的祖先不存在,用-1表示
    memset(grand,0,sizeof(grand));
    memset(gw,0,sizeof(gw));
    dfs(1);//以1为根节点建树

}
int lca(int a,int b)
{ if(depth[a] > depth[b]) swap(a, b);//保证a在b上面,便于计算
    int ans = 0;
    for(int i = N; i >= 0; i--) //类似于二进制拆分,从大到小尝试
        if(depth[a] < depth[b] && depth[grand[b][i]] >= depth[a])//a在b下面且b向上跳后不会到a上面
            ans +=gw[b][i], b=grand[b][i];//先把深度较大的b往上跳

    for(int j=N;j>=0;j--)//在同一高度了,他们一起向上跳,跳他们不相同节点,当全都跳完之后grand【a】【0】就是lca,上面有解释哈。
    {
        if(grand[a][j]!=grand[b][j])
        {   ans+=gw[a][j];
            ans+=gw[b][j];
            a=grand[a][j];
            b=grand[b][j];

        }
    }
    if(a!=b)//a等于b的情况就是上面土色字体的那种情况
    {
        ans+=gw[a][0],ans+=gw[b][0];
    }
    return ans;
}
int main()
{ int t ;
  scanf("%d",&t);
  while(t--)
  {   scanf("%d%d",&n,&m);
      for(int i=1;i<n;i++)
      {
          int x,y,w;
          scanf("%d%d%d",&x,&y,&w);
          addedge(x,y,w);
      }
     Init();
     for(int i=1;i<=m;i++)
     {
         int x,y;
         scanf("%d%d",&x,&y);
         printf("%d\n",lca(x,y));
     }
  }
}


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/127140.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • grid布局方式_grid网格布局

    grid布局方式_grid网格布局GridBagConstraints特征:由GridBagConstraints类实现的布局管理器称为网格组布局管理器,它实现了一个动态的矩形网格,这个矩形风格由无数个矩形单元格组成,每个组件可以占用一个或多个这样的单元格。动态矩形网格:可以根据实际需要随意增减矩形网格的行数和列数。它实现的矩形网格的绘制方向由容器决定,网格的索引从0开始。下面写一个测试方法来讲解GridBagC

  • creo每次都要配置config_config配置中心

    creo每次都要配置config_config配置中心前言每个测试用例都应该有config部分,可以配置用例级别。比如name、base_url、variables、verify、export等等案例演示fromhttprunnerimport

  • Matlab GUI上位机界面实现串口通信

    Matlab GUI上位机界面实现串口通信MatlabGUI因项目需求,不得不学的又杂又浅,趁着还没彻底忘记,写下来一些关键注意点。命令行窗口输入guide→BlankGUI→确定根据自己的需求,拖动选择对应的工具,如下图所示双击每一个对象,就可以弹出其检查器,修改其属性,字体大小、粗细、位置等,其中最关键的是两个,一是String,二是Tag,String是用来修改对象中的文字,Tag是所调用的代码名,这个要好的…

  • 台式机dp接口在哪(主机没有dp接口怎么办)

    导读:成就3471和战99,均为品牌成套机,价格4千元左右,是否值得入手?请往下看:戴尔(DELL)成就3471:1、硬件点评下:主板:小型机箱,主板接口很丰富,M2.0的Pic-E接口,sata接口也有预留,内存槽也可以加内存,PIC16槽也可以扩充显卡,但机箱过小,需要额外注意;DDR4接口,海力士8G2666HMZ内存办公够用;电源:没挖到官方数据,但考虑显卡不好扩容,我们基本上可以忽略…

  • Java进阶:java开源商城系统源码

    Java进阶:java开源商城系统源码正文ZooKeeper很流行,有个基本的疑问:ZooKeeper是用来做什么的?之前没有ZK,为什么会诞生ZK?OK,解答一下上面的疑问:(下面是凭直觉说的)ZooKeeper是用于简化分布式应用开发的,对开发者屏蔽一些分布式应用开发过程中的底层细节ZooKeeper对外暴露简单的API,用于支持分布式应用开发ZooKeeper在提供上述功能的同时,其还是一个高性能、高可用、高可靠的分布式集群上面说这么多,总结一下,ZK能解决分布式应用开发的问题,ZK能很好的解决

  • keyvaluepair_KeyValuePair用法(转)

    keyvaluepair_KeyValuePair用法(转)C#KeyValuePair的用法。结构体,定义可设置或检索的键/值对。也就是说我们可以通过它记录一个键/值对这样的值。比如我们想定义一个ID(int类型)和Name(string类型)这样的键/值对,那么可以这样使用。//////设置键/值对//////privateKeyValuePairSetKeyValuePair(){intintKey=1;stringstrV…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号