我是靠谱客的博主 懦弱往事,这篇文章主要介绍树形结构 —— 树与二叉树 —— 树的中心【概述】【DFS】【树形 DP】【变形问题】,现在分享给大家,希望可以做个参考。

【概述】

树的中心问题是指:当给出 n 个结点与 n-1 条边后,要选定一个点作为整棵树的根结点,使得从该点到每个叶结点的最长路径最短。

树的中心问题主要有两种方法:DFS/BFS 进行搜索、树形 DP 进行状态转移

【DFS】

根据树的中心问题的描述,显然可以知道,树的中心一定在树的直径上,而且趋于终点,否则它的最远距离只会更远。

因此,我们在利用 DFS 寻找树的直径的同时,对于直径的两个端点 st、ed,分别求其到每个点的距离 disSt[i]、disEd[i]

最后,对每个点进行更新,求最小距离 minn=min(minn,max(disSt[i],disEd[i])) 即可

复制代码
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
63
64
65
66
67
68
69
70
71
72
73
74
75
struct Edge { int to, val; int next; Edge(){} Edge(int to,int val,int next):to(to),val(val),next(next){} } edge[N]; int n; int head[N], tot; int diameter,maxx, id; int dis[N], disSt[N], disEd[N]; void addEdge(int from, int to, int val) { edge[++tot].to = to; edge[tot].val = val; edge[tot].next = head[from]; head[from] = tot; } void dfs(int x, int father) { for (int i = head[x]; i != -1; i = edge[i].next) { int y = edge[i].to; int val = edge[i].val; if (y == father) continue; dis[y] = dis[x] + val; if(dis[y]>maxx){ maxx = dis[y]; id = y; } dfs(y, x); } } void calcDiameter(){ //第一遍dfs maxx = 0; id = 1; dfs(1, 0); int st = id; //第二遍dfs maxx = 0; dis[st] = 0; dfs(st, 0); int ed = id; diameter = maxx; //树的直径 for (int i = 1; i <= n; i++) disSt[i] = dis[i]; dis[ed] = 0; dfs(ed, 0); for (int i = 1; i <= n; i++) disEd[i] = dis[i]; } int main() { scanf("%d", &n); memset(head, -1, sizeof(head)); for (int i = 1; i <= n - 1; i++) { int x, y, val; scanf("%d%d%d", &x, &y, &val); addEdge(x, y, val); addEdge(y, x, val); } calcDiameter(); int pos, minn = INF; for (int i = 1; i <= n; ++i) { if (minn > max(disSt[i], disEd[i])){ minn = max(disSt[i], disEd[i]); pos = i; } } printf("%d %dn", pos, minn); return 0; }

【树形 DP】

利用树形 DP 求解时,我们需要维护每个点 i 到所有叶结点的最长距离 up[i],从而去更新树的中心。

由于采用树形 DP 的方法,在求树的直径时已经知道如何维护每个结点 i 到其子树的叶结点的最长距离 dis1[i] 与次长距离 dis2[i],那么接下来就要考虑如何维护这个点向上的最远距离 up[i]

依旧用 pos1[x] 表示 dis1[x] 在哪个点更新,pos2[x] 表示 dis2[x] 在哪个点更新,再求出树的直径后,再次进行一次 DFS 即可

复制代码
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
63
64
65
66
67
68
69
70
71
struct Edge { int to, val; int next; Edge(){} Edge(int to,int val,int next):to(to),val(val),next(next){} } edge[N]; int n; int head[N], tot; int dis1[N], dis2[N];//分别维护第i个点的最长链、次长链 int pos1[N],pos2[N];//分别维护dis1[i]、dis2[i]从哪个点更新 int up[N];//点i到所有叶结点的最远距离 void addEdge(int from, int to, int val) { edge[++tot].to = to; edge[tot].val = val; edge[tot].next = head[from]; head[from] = tot; } void dfs(int x, int father) { for (int i = head[x]; i != -1; i = edge[i].next) { int y = edge[i].to; int val = edge[i].val; if (y == father) continue; dfs(y, x); if (dis1[y] + val > dis1[x]) { dis2[x] = dis1[x]; dis1[x] = dis1[y] + val; pos2[x] = pos1[x]; pos1[x] = y; } else if (dis1[y] + val > dis2[x]) { dis2[x] = dis1[y] + val; pos2[x] = y; } } } void dfsCenter(int x, int father) { for (int i = head[x]; i != -1; i = edge[i].next) { int y = edge[i].to; int val = edge[i].val; if (y == father) continue; if (pos1[x] != y) up[y] = max(dis1[x], up[x]) + val; else up[y] = max(dis2[x], up[x]) + val; dfsCenter(y, x); } } int main() { scanf("%d", &n); memset(head, -1, sizeof(head)); for (int i = 1; i <= n - 1; i++) { int x, y, val; scanf("%d%d%d", &x, &y, &val); addEdge(x, y, val); addEdge(y, x, val); } dfs(1, 0); dfsCenter(1, 0); int pos, minn = INF; for (int i = 1; i <= n; i++) { if (minn > max(up[i], dis1[i])) { minn = max(up[i], dis1[i]); pos = i; } } printf("%d %d", pos, minn); return 0; }

【变形问题】

树的中心问题,最常见的一种变型问题是:给出一棵树 n 个点的点权与 n-1 条边的边权,求树的最小代价的和,定义代价为树中两点距离乘以点的点权

该问题是最常见的,一般数据规模较小,利用 Floyd 算法即可解决。

复制代码
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
int n; int G[N][N], node[N], sum[N]; int main() { cin >> n; for (int i = 1; i <= n; i++) //点权 cin >> node[i]; for (int i = 1; i <= n - 1; i++) { //边权 int x, y, dis; cin >> x >> y >> dis; G[x][y] = dis; G[y][x] = dis; } //Floyd记录各点间的距离 for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (G[i][j] > G[i][k] + G[k][j]) G[i][j] = G[i][k] + G[k][j]; //枚举求最小代价 int minn = INF; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) sum[i] += G[i][j] * node[j]; if (sum[i] < minn) minn = sum[i]; } cout << minn << endl; return 0; }

 

最后

以上就是懦弱往事最近收集整理的关于树形结构 —— 树与二叉树 —— 树的中心【概述】【DFS】【树形 DP】【变形问题】的全部内容,更多相关树形结构内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部