hihocoder1069最近公共祖先·三(LCA在线算法--DFS+RMQ-ST)

本文详细介绍了LCA在线算法的原理及其实现过程,通过DFS遍历获取节点信息,并运用RMQ-ST算法快速找到最短路径中深度最小的节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树上任意两点的最近祖先,必定就是这两个节点的最短路径上深度最小的那个点。

例如:下图中,节点7和5,其最短路径为7--4--1--5, 这条路径上深度最小的点为节点1,其深度为1.节点1即为节点7和5的LCA。

因此,要找到任意两个节点的LCA,只需要先找到上述最短路径,再找到最短路径中深度最小的点。而这下面所述LCA在线算法所做的事。

LCA在线算法描述(以上图为例):

1.获得“最短路径”(并不是真正的一条路径,包含其他节点,但不影响算法的正确性)

采用DFS遍历整棵树,得到以下数据:

(1)遍历序列p:0  1  3  1  4  7  4  8  4  1  5  1  0  2  6  2  0

(2)各节点的深度序列        depth: 0  1   1   2   2  2   2    3  3

(3)各节点在序列p中首次出现的位置序列pos: 0  1  13  2  4  10  14  5  7

有了以上数据,假设现在我们要求节点7和5的最短路径,我们可以这样做:

(1)首先,从pos序列中获得节点7和节点5在p序列中第一次出现的位置分别为:pos[7] = 5, pos[5] = 10;

(2)得到p序列中[5, 10]这一段子序列s:7  4  8  4  1  5

(3)s序列中深度最小的点即节点1就是我们要找的节点7和节点5的LCA。

注意到,此时的s序列并非是从节点7到节点5的一条最短路径,它除了包含7到5的最短路径上的节点外,还包含了一些其他的节点,但这些其他的节点都是以节点1为根的子树上的节点,他们的深度都比节点1大,不影响我们算法对正确结果的求解。

2.如何快速的获得一段序列中深度最小的节点

求解区间最值的问题是我们所熟悉的经典的RMQ问题,用RMQ-ST算法即可。由于RMQ-ST算法是在线的,故我们的LCA算法也是在线的。

下面是我的代码实现:

 1 #include <iostream>
 2 #include <string>
 3 #include <map>
 4 #include <vector>
 5 #include <algorithm>
 6 #include <cmath>
 7 
 8 using namespace std;
 9 
10 #define MAXN 100005
11 
12 map<string, int> mp;
13 string name[2*MAXN];
14 vector<int> v[2*MAXN];
15 int p[4*MAXN], depth[2*MAXN], pos[2*MAXN];
16 int pre_cal[4*MAXN][20];
17 int cnt, n, m;
18 
19 void dfs(int i, int d)
20 {
21     pos[i] = cnt;
22     p[cnt++] = i;
23     depth[i] = d;
24     if(v[i].empty()) return;
25     for(int j=0; j<v[i].size(); ++j)
26     {
27         dfs(v[i][j], d+1);
28         p[cnt++] = i;
29     }
30 }
31 
32 void rmq()
33 {
34     for(int i=0; i<4*MAXN; ++i) pre_cal[i][0] = i;
35     for(int j=1; (1<<(j-1))<4*MAXN; ++j)
36         for(int i=0; i+(1<<(j-1))<4*MAXN; ++i)
37                 pre_cal[i][j] = depth[p[pre_cal[i][j-1]]]<depth[p[pre_cal[i+(1<<(j-1))][j-1]]]?pre_cal[i][j-1]:pre_cal[i+(1<<(j-1))][j-1];
38 }
39 
40 string lca(int a, int b)
41 {
42     int k = floor(log(b-a+1)/log(2));
43     int x = pre_cal[a][k], y = pre_cal[b-(1<<k)+1][k];
44     return depth[p[x]]<depth[p[y]]?name[p[x]]:name[p[y]];
45 }
46 
47 void init()
48 {
49     cnt = 0;
50     mp.clear();
51     for(int i=0; i<2*MAXN; ++i) v[i].clear();
52 }
53 
54 int main()
55 {
56     string name1, name2;
57     while(cin>>n)
58     {
59         init();
60         while(n--)
61         {
62             cin>>name1>>name2;
63             if(mp.find(name1)==mp.end())
64             {
65                 mp[name1] = cnt;
66                 name[cnt++] = name1;
67             }
68             if(mp.find(name2)==mp.end())
69             {
70                 mp[name2] = cnt;
71                 name[cnt++] = name2;
72             }
73             v[mp[name1]].push_back(mp[name2]);
74         }
75         cnt = 0;
76         dfs(0, 0);
77         rmq();
78         cin>>m;
79         while(m--)
80         {
81             cin>>name1>>name2;
82             int a = mp[name1], b = mp[name2];
83             cout<<(pos[a]<pos[b]?lca(pos[a], pos[b]):lca(pos[b], pos[a]))<<endl;
84         }
85     }
86 
87     return 0;
88 }

题目链接:http://hihocoder.com/problemset/problem/1069

 

转载于:https://www.cnblogs.com/pczhou/p/4297132.html

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了轴陀螺仪和轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值