Nearest Common Ancestors
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 19728   Accepted: 10460

Description

A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: 



 

In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor
of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node
x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common
ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is. 



For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest
common ancestor of y and z is y. 



Write a program that finds the nearest common ancestor of two distinct nodes in a tree. 


Input

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,...,
N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers
whose nearest common ancestor is to be computed.

Output

Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.

Sample Input

  1. 2
  2. 16
  3. 1 14
  4. 8 5
  5. 10 16
  6. 5 9
  7. 4 6
  8. 8 4
  9. 4 10
  10. 1 13
  11. 6 15
  12. 10 11
  13. 6 7
  14. 10 2
  15. 16 3
  16. 8 1
  17. 16 12
  18. 16 7
  19. 5
  20. 2 3
  21. 3 4
  22. 3 1
  23. 1 5
  24. 3 5

Sample Output

  1. 4
  2. 3

Source

本题是一个多叉树,然后求两点的近期公共单亲节点。

就是典型的LCA问题。这是一个非常多解法的,并且被研究的非常透彻的问题。

原始的解法:从根节点往下搜索,若果搜索到两个节点分别在一个节点的两边。那么这个点就是近期公共单亲节点了。

Trajan离线算法:首次找到两个节点的时候,假设记录了他们的最低单亲节点,那么答案就是这个最低的单亲节点了。

问题是怎样有效记录这个最低单亲节点,并有效依据遍历的情况更新,这就是利用Union Find(并查集)记录已经找到的节点,并及时更新最新訪问的节点的当前最低单亲节点。

就是并查集的灵活运用啦,假设学会了并查集那么学这个算法是不难的了。

2015-1-24 update:

以下括号的分析好像有点问题:

(以下是简单思路差点儿相同是暴力法的解法,只是事实上相对本题来说就是一次查询,故此这个也是不错的方法,并且平均时间效率只是O(lgn),应该是非常快的了。

只是有思想和这个差点儿相同的。可是更加省内存的方法,就是从须要查找的节点往单亲节点查找,那么速度是一样的,只是省内存,由于仅仅须要记录一个父母节点就能够了。并且程序会简洁点。)

这种方法的基本思想是:

如果要查找u,v的LCA,

递归深度遍历整棵树,这个时候有三种情况:

1. 假设没哟找到u或者v当中一个节点。那么就返回0;

2 假设找到了u或v的当中一个节点。那么就返回找到的节点

3 假设找到u且找到v两个节点。那么就返回其父母节点。而这个父母节点正好是LCA,为什么呢?由于这个是逐层递归上去的算法,仅仅有在u和v的分叉节点能找到两个非零返回值,其它情况都仅仅能找到一个或者0个非零返回值。巧妙地利用了递归的特点。把LCA作为了终于的返回值。

由于最坏情况须要递归整棵树。故此这个算法的时间效率事实上是O(n),n为整棵树的节点数。

故此本算法尽管AC了,可是事实上时间效率还是蛮低的。

之前分析说是O(lgn)是不正确的。不好意思。假设误导了某些读者,那么表示抱歉。

还好以下算法是没错的。

本算法对递归的理解还是要求挺高的,对于刚開始学习的人还是有点难度。

  1. int const MAX_N = 10001;
  2.  
  3. struct Node
  4. {
  5. bool notRoot;
  6. vector<int> children;
  7. };
  8.  
  9. Node Tree[MAX_N];
  10. int N;
  11.  
  12. int find(int r, int lNode, int rNode)
  13. {
  14. if (!r) return 0;
  15. if (r == lNode) return r;
  16. if (r == rNode) return r;
  17.  
  18. vector<int> found;
  19. for (int i = 0; i < (int)Tree[r].children.size(); i++)
  20. {
  21. found.push_back(find(Tree[r].children[i], lNode, rNode));
  22. }
  23. int u = 0, v = 0;
  24. for (int i = 0; i < (int)found.size(); i++)
  25. {
  26. if (found[i] != 0)
  27. {
  28. if (u) v = found[i];
  29. else u = found[i];
  30. }
  31. }
  32. if (v) return r;
  33. return u;
  34. }
  35.  
  36. void solve()
  37. {
  38. scanf("%d", &N);
  39. memset(Tree, 0, sizeof(Tree));
  40.  
  41. int u, v;
  42. for (int i = 1; i < N; i++)
  43. {
  44. scanf("%d %d", &u, &v);
  45. Tree[u].children.push_back(v);
  46. Tree[v].notRoot = 1;
  47. }
  48. int root = 0;
  49. for (int i = 1; i <= N; i++)
  50. {
  51. if (!Tree[i].notRoot)
  52. {
  53. root = i;
  54. break;
  55. }
  56. }
  57.  
  58. scanf("%d %d", &u, &v);
  59. int r = find(root, u, v);
  60. printf("%d\n", r);//if (lin && rin) 必定是存在点。故此无需推断
  61. }
  62.  
  63. int main()
  64. {
  65. int T;
  66. scanf("%d", &T);
  67. while (T--)
  68. {
  69. solve();
  70. }
  71. return 0;
  72. }

以下是Tarjan离线算法,效率应该和上面是一样的。多次查询的时候就能提高效率。只是实际执行比上面快。

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <vector>
  4. using std::vector;
  5. int const MAX_N = 10001;
  6.  
  7. struct Node
  8. {
  9. bool notRoot;
  10. bool vis;
  11. vector<int> children;
  12. };
  13.  
  14. Node Tree[MAX_N];
  15. int N;
  16. int u, v;
  17. int parent[MAX_N];
  18.  
  19. inline int find(int x)
  20. {
  21. if (!parent[x]) return x;
  22. return parent[x] = find(parent[x]);
  23. }
  24.  
  25. inline void unionTwo(int p, int x)
  26. {
  27. p = find(p);
  28. x = find(x);
  29. if (p == x) return ;
  30. parent[x] = p;
  31. }
  32.  
  33. bool LCATarjan(int root)
  34. {
  35. Tree[root].vis = true;
  36. if (root == u && Tree[v].vis == true)
  37. {
  38. printf("%d\n", find(v));
  39. return true;
  40. }
  41. if (root == v && Tree[u].vis == true)
  42. {
  43. printf("%d\n", find(u));
  44. return true;
  45. }
  46. for (int i = 0; i < (int)Tree[root].children.size(); i++)
  47. {
  48. if (LCATarjan(Tree[root].children[i])) return true;
  49. unionTwo(root, Tree[root].children[i]);
  50. }
  51. return false;
  52. }
  53.  
  54. void solve()
  55. {
  56. scanf("%d", &N);
  57. memset(Tree, 0, sizeof(Tree));
  58. memset(parent, 0, sizeof(parent));
  59.  
  60. for (int i = 1; i < N; i++)
  61. {
  62. scanf("%d %d", &u, &v);
  63. Tree[u].children.push_back(v);
  64. Tree[v].notRoot = 1;
  65. }
  66. int root = 0;
  67. for (int i = 1; i <= N; i++)
  68. {
  69. if (!Tree[i].notRoot)
  70. {
  71. root = i;
  72. break;
  73. }
  74. }
  75. scanf("%d %d", &u, &v);
  76. LCATarjan(root);
  77. }
  78.  
  79. int main()
  80. {
  81. int T;
  82. scanf("%d", &T);
  83. while (T--)
  84. {
  85. solve();
  86. }
  87. return 0;
  88. }

POJ 1330 Nearest Common Ancestors LCA题解的更多相关文章

  1. POJ.1330 Nearest Common Ancestors (LCA 倍增)

    POJ.1330 Nearest Common Ancestors (LCA 倍增) 题意分析 给出一棵树,树上有n个点(n-1)条边,n-1个父子的边的关系a-b.接下来给出xy,求出xy的lca节 ...

  2. poj 1330 Nearest Common Ancestors lca 在线rmq

    Nearest Common Ancestors Description A rooted tree is a well-known data structure in computer scienc ...

  3. poj 1330 Nearest Common Ancestors LCA

    题目链接:http://poj.org/problem?id=1330 A rooted tree is a well-known data structure in computer science ...

  4. POJ 1330 Nearest Common Ancestors (LCA,倍增算法,在线算法)

    /* *********************************************** Author :kuangbin Created Time :2013-9-5 9:45:17 F ...

  5. POJ 1330 Nearest Common Ancestors(LCA模板)

    给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果 ...

  6. POJ - 1330 Nearest Common Ancestors(基础LCA)

    POJ - 1330 Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000KB   64bit IO Format: %l ...

  7. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  8. POJ 1330 Nearest Common Ancestors(lca)

    POJ 1330 Nearest Common Ancestors A rooted tree is a well-known data structure in computer science a ...

  9. POJ 1330 Nearest Common Ancestors 倍增算法的LCA

    POJ 1330 Nearest Common Ancestors 题意:最近公共祖先的裸题 思路:LCA和ST我们已经很熟悉了,但是这里的f[i][j]却有相似却又不同的含义.f[i][j]表示i节 ...

随机推荐

  1. 如何上传base64编码图片到七牛云

    接口说明 POST /putb64/<Fsize>/key/<EncodedKey>/mimeType/<EncodedMimeType>/crc32/<Cr ...

  2. HTML5API___Web Storage

    Web Storage 是html5的本地存储规范 支持:移动平台基本支持 (opera mini除外) ie8+ff chrome 等 支持 它包含2个: sessionStorage 会话存储   ...

  3. OpenCV 开发环境环境搭建(win10+vs2015+opencv 3.0)

    OpenCV 3.0 for windows(下载地址:http://opencv.org/): 本测试中,OpenCV安装目录:D:\Program Files\opencv,笔者操作系统为64位. ...

  4. spring 加载配置文件的相关配置总结

    PropertyPlaceholderConfigurer      注意: Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer(或<context:p ...

  5. 【转】CxImage图像库的使用

    CxImage下载地址:http://www.codeproject.com/KB/graphics/cximage/cximage600_full.zip 作者:Davide Pizzolato C ...

  6. 浮点数(double)的优势

    用 double 处理大数据 , 即使字符串位数超百位,同样可以用 sscanf 或 atof 来处理,却不会变成负数,原因是浮点数可以用指数表示,但精度会丢失,下面介绍利用这个性质解决的一些问题: ...

  7. 简单的mvvm light 应用

      public  class MainStudentModel:ViewModelBase    { //实体        private StudentModel stu = new Stude ...

  8. JS字符串方法总结整理

    //javascript字符串方法总结   1.String.charAt(n)      //取得字符串中的第n个字符   2.String.charCodeAt(n)  //取得字符串中第n个字符 ...

  9. A Byte of Python 笔记(8)

    第10章  解决问题——编写一个 python 脚本 程序功能:为所有重要文件创建备份 设计: 1.需要备份的文件和目录由一个列表指定 2.备份应该保存在主备份目录中 3.文件备份称一个 zip 文件 ...

  10. KVO 的使用和举例

    KVO(key-value Observer),通过命名可以联想到,一个监视着监视着键值配对,让一个对象A来监视另一个对象B中的键值,一旦B中的受监视键所对应的值发生了变化,对象A会进入一个回调函数, ...