• 【算法】距离(最近公共祖先节点)


    题目

    给出 n 个点的一棵树,多次询问两点之间的最短距离。

    注意:

    • 边是无向的。
    • 所有节点的编号是 1,2,…,n。
    输入格式

    第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;

    下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;

    再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。

    树中结点编号从 1 到 n。

    输出格式

    共 m 行,对于每次询问,输出一行询问结果。

    数据范围

    2 ≤ n ≤ 10^4
    1 ≤ m ≤ 2 × 10^4
    0 < k ≤ 100
    1 ≤ x , y ≤ n

    思路

     我们以以下样例来建一张图

    1. 样例:
    2. 10 0
    3. 1 2
    4. 1 3
    5. 2 4
    6. 2 5
    7. 3 6
    8. 3 7
    9. 4 8
    10. 4 9
    11. 8 10

            首先我们假定点1为根节点,求出所有节点到点1的最短距离dist[ j ]。 

            我们可以假设点1为根节点,往下深度优先遍历每一个节点,只有当某一个节点的所有子节点都被便利之后才会更新其祖先节点,所以在这个点 a 的所有子节点没有遍历结束之前, a 的所有子节点的祖先都是节点 a 。易得,当求的两个点 x , y 都是属于点 a 的孙子结点的时候,x与y的距离为dist[ i ] + dist[ j ] - dist[ a ] * 2;

    代码 

    1. #include
    2. using namespace std;
    3. typedef pair<int,int> PII;
    4. const int N = 20010, M = N * 2;
    5. int n,m;
    6. int h[N],e[M],w[M],ne[M],idx;
    7. int res[N];
    8. int dist[N];
    9. int st[N];
    10. int p[N];
    11. vector query[N];
    12. void add(int a,int b,int c)// 加点函数,使用邻接表储存该图
    13. {
    14. e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++;
    15. }
    16. int find(int x)// 并查集板子
    17. {
    18. if(p[x] != x) p[x] = find(p[x]);
    19. return p[x];
    20. }
    21. void dfs(int u,int fa)// 初始每个点到根节点的距离
    22. {
    23. for(int i = h[u]; ~i ; i = ne[i])
    24. {
    25. int j = e[i];
    26. if(j == fa) continue;// 因为是无向边,所以会有一条边指向该点的父节点。
    27. dist[j] = dist[u] + w[i];// 子节点距离根节点的距离为父节点加上父节点到该点的距离
    28. dfs(j,u);//使用该点继续初始其他节点
    29. }
    30. }
    31. void tarjan(int u)// 该题核心函数
    32. {
    33. st[u] = 1;
    34. for(int i = h[u];~i; i = ne[i])// 每个点的祖先都是它的父节点
    35. {
    36. int j = e[i];
    37. if(!st[j])
    38. {
    39. tarjan(j);// 以j为祖先节点,遍历所有j的所有子节点
    40. p[j] = u;//将点j的所有子节点遍历完成之后,就更新点j的祖先节点
    41. }
    42. }
    43. for(auto item : query[u])
    44. {
    45. int y = item.first,id = item.second;
    46. if(st[y] == 2)// 如果点y已经完成遍历,则可以进行求距离操作
    47. {
    48. int anc = find(y);
    49. res[id] = dist[u] + dist[y] - dist[anc] * 2;
    50. }
    51. }
    52. st[u] = 2;//表示该点祖先节点已经更新,且所有子节点都已经完成遍历
    53. }
    54. int main()
    55. {
    56. cin >> n >> m;
    57. memset(h,-1,sizeof(h));
    58. for(int i = 1; i < n; i ++)//输入n-1条无向边
    59. {
    60. int a,b,c;
    61. cin >> a >> b >> c;
    62. add(a,b,c),add(b,a,c);
    63. }
    64. for(int i = 0; i < m; i ++)//输入m个询问
    65. {
    66. int a,b;
    67. cin >> a >> b;
    68. if(a == b) continue;
    69. query[a].emplace_back(b,i);
    70. query[b].emplace_back(a,i);
    71. }
    72. for(int i = 1; i <= n; i ++) p[i] = i;// 并查集初始化每个点所属的集合
    73. dfs(1,-1);// 假设点1为该树的根节点
    74. tarjan(1);
    75. for(int i = 0; i < m; i ++) cout << res[i] << endl;
    76. return 0;
    77. }

  • 相关阅读:
    程序员如何学习开源项目,这篇文章告诉你
    Mysql__安装教程
    记一次 .NET 某电子厂OA系统 非托管内存泄露分析
    2019史上最全java面试题题库大全800题含答案
    【css】关于css样式中border-radius和border-image不兼容问题
    java毕业设计数码产品导购网站mybatis+源码+调试部署+系统+数据库+lw
    python提高运算速度的方法:内存缓存+磁盘缓存
    Backtracking algorithm梳理
    Python之人机猜拳游戏
    多快好省,低门槛AI部署工具FastDeploy测试版来了!
  • 原文地址:https://blog.csdn.net/littlegengjie/article/details/134449717