树的直径
树的直径是树上最长的一条链,当然这条链不唯一,所以一棵树可能有多条直径。直径由两个顶点u,v决定,若有一条直径(u,v),则满足以下性质:
- u,v的度数均为1(叶子节点)
- 在以任意一个点为根的树上,u,v中必然存在一个点作为最深的叶子结点。
- 深度就是点距离根节点的距离
利用上述性质求直径长度:
第一遍DFS使用点1找到最深的点u(多个则随机选一个),然后从u出发做第二遍DFS找到最深的点v(多个则随机选个即可),得到的<u,v>即为一条直径上述方法要求边权值不为负,负权则利用树形DP求解
例题分析
直径代码模板
import sys
#深搜最好都要加一下
sys.setrecursionlimit(100000)
input = sys.stdin.readline
def dfs(u, fa, pre = None):
global S#设置最深叶节点的索引
if d[u] > d[S]:
S = u
for v, w in G[u]:
if v == fa:
continue
#记录节点的深度,w是路径的长度
d[v] = d[u] + w
#因为第一遍遍历的不是直径,我们要从第二遍叶子节点开始记录
if pre:
pre[v] = u
dfs(v,u,pre)
n = int(input())
#记录树
G = [[] for i in range(n + 1)]
#记录深度(路径的权重和)
d = [0] * (n + 1)
#记录路径上的节点
pre = [0] *(n + 1)
for _ in range(n - 1):
u, v, w = map(int, input().split())
G[u].append([v, w])
G[v].append([u, w])
S = 1
#从根节点开始遍历到叶子节点
dfs(1, 0)
d[S] = 0
#以叶子节点为根,遍历到另外一个叶子节点
dfs(S, 0, pre)
#记录路径的权重和
L = d[S]
#获取直径所在的边
L_list = set()
#如果没有遍历到根节点就一直循环
#寻找直径上面的所有点
while S != 0:
L_list.add(S)
S = pre[S]
for u in L_list:
for i, (v, w) in enumerate(G[u]):
if v in L_list:
G[u][i] = [v, w - 1]
S = 1
dfs(1, 0)
d[S] = 0
#边的权重减去一之后的路径权重和
dfs(S, 0)
L2 = d[S]
print(L)
#公共边的路径长度
print(L - L2)
树的重心
性质分析
利用性质1求树的重心
代码复现
import sys
sys.setrecursionlimit(100000)
input = sys.stdin.readline
#mss[x]表示x的所有子树,包括父节点大小的最大值
#sz数组统计每个子树的大小
def dfs(u, fa):
for v in G[u]:
if v == fa:
continue
dfs(v, u)
#统计每个点的子树的节点个数
sz[u] +=sz[v]
mss[u] = max(mss[u],sz[v])
mss[u] = max(mss[u], n - sz[u])
n = input()
G = [[] for i in range(n + 1)]
sz = [0] * (n + 1)
mss = [0] * (n + 1)
for _ in range(n - 1):
u, v = map(int, input().split())
G[u].append(v)
G[v].append(u)
dfs(1, 0)
root = mss.index(min(mss[1:]))
感悟
还是多模拟这个过程你就知道代码的奥妙了!啊哈哈哈
蓝桥杯云课学习笔记分享,欢迎大佬们批评指正!
一直在进步就好咯!
by 闻不多