AcWing 3555:二叉树(多次询问两个结点之间的最短路径长度) ← DFS

【题目来源】
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 1

out:
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/



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值