A - Nearest Common Ancestors(邻接表的简单应用)

            A - Nearest Common Ancestors

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
2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5
Sample Output
4
3

题目的大意在这里就不说啦,大家自己翻译一下 锻炼一下英语 ,或者 有道翻译都行,写这个题的时候 刚学啦RMQ 和 LCE 求解 共同祖先的问题,其实 思路是很简单的,但是 构建树的时候需要用到邻接表 来存储数据。简单说一下 解题的思路把,就是 当你将树构建好,而且用一个数组F【N】来存储 每一个 节点的父节点,然而应为 dfs 的时候要从根节点开始, 这里还要求一下 根节点,大家想一下 是不是除了根节点以外的节点 是不是都有 它的 父节点,这里 利用F[N] 循环一下就可以求出 根节点啦, 然后在 dfs 的时候 用一个数组depth[N] 存储 每一个节点的 深度 (根节点的深度为0), 好啦 所有的准备工作已经做好啦,接下来该 求解 公共祖先啦,下面的 程序就简单啦 ,大家 看看下面的代码就OK 啦。另外 提醒一下,vector arr[N] 中 这个数组的 下标 是从 0 开始的 ,如果 你的数据不是从零开始的 那么 在 dfs 中 要 i = 0 的情况 用 continue 跳过 就行啦;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

int f[10010];// 存储父节点
int d[10010];// 存储每一个节点的深度
vector<int> arr[10010];// 用邻接表 构建 树

void dfs(int u, int dep)
{
    d[u] = dep;
    for(vector<int>::iterator it = arr[u].begin(); it != arr[u].end(); it++)
        if(*it == 0) continue;// 跳过  it = 0 的时候 跳过
        else dfs(*it, dep+1);
}

int main()
{
    int t; cin>>t;

    while(t--)
    {
        int x, y, n;

        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            arr[i].clear();
            f[i] = -1;
        }
        for(int i = 1; i < n; i++)
        {
            scanf("%d%d", &x, &y);
            arr[x].push_back(y);
            f[y] = x;
        }

        int k;
        for(k = 1; f[k] >= 0; k++);// 找出 根节点
        dfs(k, 0);

        scanf("%d%d", &x, &y);
        while(x != y)
        {
            if(d[x] > d[y])
                x = f[x];
            else
                y = f[y];
        }

        printf("%d\n", x);
    }

    return 0;
}

下面 是我新学的 一个方法 , 是根据 一直 回溯 祖先值, 并 把 回溯过程中的祖先值 都标记一下, 然后在回溯 另一个 节点的 祖先值 ,在回溯的过程中 如果 遇到一个 祖先值 已经被标记啦, 就将这个 祖先值 输出 ,这个就是所求解的 共同的祖先;

下面请看代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct TreeNode
{
    int father;
    int mark;
}tree[10005];

void LCA(int x, int y)
{
    tree[x].mark = true;
    x = tree[x].father;
    while(x != tree[x].father)
    {
        tree[x].mark = true;
        x = tree[x].father;
    }
    while(y != tree[y].father)
    {
        if(tree[y].mark == true)
            break;
        y = tree[y].father;
    }
    printf("%d\n", y);// 这个如果放在里面的话 会WA 应为 while  循环有时候 进不去的  那么就会出现没有输出的的情况
}

int main()
{
    int t; cin>>t;

    while(t--)
    {
        int n, x, y;

        scanf("%d", &n);
        for(int i = 0; i <= n; i++)// 先进性初始化
        {
            tree[i].father = i;
            tree[i].mark = false;
        }
        for(int i = 1; i < n; i++)
        {
            scanf("%d%d", &x, &y);
            tree[y].father = x;
        }

        scanf("%d%d", &x, &y);
        LCA(x, y);
    }

    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值