0%

挑战程序设计竞赛-day14

最近公共祖先(LCA, lowest common ancestor)

基于二分搜索的算法

1580440175576

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 如果只计算一次LCA的话,这就足够了,算法的复杂度为O(n)
// 输入
vector<int> G[MAX_V]; // 图的邻接表表示
int root; // 根节点的编号
int parent[MAX_V]; // 父亲节点(根节点的父亲记为-1)
int depth[MAX_V]; // 节点的深度

void dfs(int v, int p, int d) {
parent[v] = p;
depth[v] = d;
for(int i = 0; i < G[v].size(); i++) {
if(G[v][i] != p) {
dfs(G[v][i], v, d + 1);
}
}
}

// 预处理
void init() {
// 预处理出parent和depth
dfs(root, -1, 0);
}

// 计算u和v的LCA
int lca(int u, int v) {
// 让u和v向上走到同一深度
while(depth[u] > depth[v]) {
u = parent[u];
}
while(depth[v] > depth[u]) {
v = parent[v];
}
// 让u和v向上走到同一节点
while(u != v) {
u = parent[u];
v = parent[v];
}
return u;
}

1580440243333

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
using namespace std;
const int MAX_V = 100;
const int MAX_LOG_V = 10;

// 如果只计算一次LCA的话,这就足够了,算法的复杂度为O(n)
// 输入
vector<int> G[MAX_V]; // 图的邻接表表示
int root; // 根节点的编号
int parent[MAX_LOG_V][MAX_V]; // v向上走2^k所到达的节点(超过根时记为-1)
int depth[MAX_V]; // 节点的深度

void dfs(int v, int p, int d) {
parent[0][v] = p;
depth[v] = d;
for(int i = 0; i < G[v].size(); i++) {
if(G[v][i] != p) {
dfs(G[v][i], v, d + 1);
}
}
}

// 预处理
void init(int V) {
// 预处理出parent[0]和depth
dfs(root, -1, 0);
// 预处理出parent
for(int k = 0; k < MAX_LOG_V; k++) {
for(int v = 0; v < V; v++) {
if(parent[k][v] < 0) {
parent[k + 1][v] = -1;
} else {
parent[k + 1][v] = parent[k][parent[k][v]];
}
}
}
}

// 计算u和v的LCA
int lca(int u, int v) {
// 让u和v向上走到同一深度
if(depth[u] > depth[v]) {
swap(u, v);
}
// 将两者深度之差表示成二进制的形式,然后从最低位开始,如果最低位为第k位,并且是1,表示应该向上走2^k步,即parent[k][v]
for(int k = 0; k < MAX_LOG_V; k++) {
if((depth[v] - depth[u]) >> k & 1) {
v = parent[k][v];
}
}
if(u == v) {
return u;
}
// 利用二分搜索计算LCA
for(int k = MAX_LOG_V - 1; k >= 0; k--) {
if(parent[k][u] != parent[k][v]) {
u = parent[k][u];
v = parent[k][v];
}
}
return parent[0][u];
}

基于RMQ的算法

1580440138731

1580440282070

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <bits/stdc++.h>
using namespace std;

const int MAX_V = 100;
// 输入
vector<int> G[MAX_V];
int root;

int vs[MAX_V * 2 - 1]; // DFS的访问顺序
int depth[MAX_V * 2 - 1]; // 节点的深度
int id[MAX_V]; // 各个顶点在vs中首次出现的下标

void dfs(int v, int p, int d, int &k) {
id[v] = k;
vs[k] = v;
depth[k++] = d;
for(int i = 0; i < G[v].size(); i++) {
if(G[v][i] != p) {
dfs(G[v][i], v, d + 1, k);
vs[k] = v;
depth[k++] = d;
}
}
}

// 预处理
void init(int V) {
// 预处理出vs、depth和id
int k = 0;
dfs(root, -1, 0, k);
// 预处理出RMQ(返回的不是最小值,而是最小值对应下标)
rmg_init(depth, V * 2 - 1);
}

// 计算u和v的LCA
int lca(int u, int v) {
return vs[query(min(id[u], id[v]), max(id[u], id[v]) + 1)];
}