【题目来源】
https://www.acwing.com/problem/content/3558/
【题目描述】
给定一个 n 个结点(编号 1∼n)构成的二叉树,其根结点为 1 号点。
进行 m 次询问,每次询问两个结点之间的最短路径长度。
树中所有边长均为 1。
【输入格式】
第一行包含一个整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 n 行,每行包含两个整数,其中第 i 行的整数表示结点 i 的子结点编号。如果没有子结点则输出 −1。
接下来 m 行,每行包含两个整数,表示要询问的两个结点的编号。
【输出格式】
每组测试数据输出 m 行,代表查询的两个结点之间的最短路径长度。
【数据范围】
1≤T≤10,
1≤n,m≤1000
【输入样例】
1
8 4
2 3
4 5
6 -1
-1 -1
-1 7
-1 -1
8 -1
-1 -1
1 6
4 6
4 5
8 1
【输出样例】
2
4
2
4
【算法分析】
此算法分析来源于:https://www.acwing.com/solution/content/127225/
本题用 le,ri 数组存储每个结点的左右儿子,用 fa 数组表示子结点的父节点,用 dep 数组表示每个结点的深度并用 dfs 初始化每个结点的深度。
推导任意两个结点直接的最短距离公式:
(1)设原有方案是两个结点都往上寻找公共祖先节点,如果不是这么做,那么一定至少有一步是往下寻找的,这样一定会导致原有方案至少多出两步,所以往上寻找一定是最优解。
(2)要找到两个结点的最近公共祖先节点,就如题目 https://www.acwing.com/problem/content/description/3435/ 一样。哪个结点的深度更大,那么就让哪个结点优先找它的父结点。
(3)最短距离公式:mind = 两个结点到最近公共祖先结点深度差的和。
【算法代码】
#include<bits/stdc++.h>
using namespace std;const int maxn=1005;
int le[maxn], ri[maxn];
int fa[maxn], dep[maxn];
int n,m;void dfs(int u) {if(u==-1) return;if(le[u]>0) dep[le[u]]=dep[u]+1;if(ri[u]>0) dep[ri[u]]=dep[u]+1;dfs(le[u]);dfs(ri[u]);
}int main() {int T;cin>>T;while(T--) {cin>>n>>m;fa[1]=1;for(int i=1; i<=n; i++) {int a,b;cin>>a>>b;le[i]=a, ri[i]=b;if(a>0) fa[a]=i;if(b>0) fa[b]=i;}dfs(1);while(m--) {int a,b;cin>>a>>b;int x=a;int y=b;while(x!=y) {if(dep[x]>dep[y]) x=fa[x];else y=fa[y];}cout<<dep[a]+dep[b]-dep[x]*2<<endl;}}return 0;
}/*
in:
1
8 4
2 3
4 5
6 -1
-1 -1
-1 7
-1 -1
8 -1
-1 -1
1 6
4 6
4 5
8 1out:
2
4
2
4
*/
【参考文献】
https://www.acwing.com/solution/content/127225/
https://www.acwing.com/solution/content/126883/
https://www.acwing.com/solution/content/127139/