其他分享
首页 > 其他分享> > 题解——Lowest Common Ancestor(16进制LCA)

题解——Lowest Common Ancestor(16进制LCA)

作者:互联网

Perfect binary trees are one of the coolest structures that computer scientists study. They have a lot of properties that make them very nice to work with. One of the nicest properties is that they can just be described by a single integerngiving the depth of the tree. For instance, the perfect binary tree forn= 3 looks like:

In general, a perfect binary tree with depthnwill have exactly 2n+1 – 1 nodes, and can be numbered by following the pattern seen above (there are of course other ways to number the nodes of the tree, but this is the scheme we will use in this problem).

A common question that arises when dealing with trees is the query of the lowest common ancestor (commonly called LCA) of two nodes in the tree. Formally, the LCA ofxandyis the nodezof greatest depth in the tree such thatzis an ancestor ofxandy. Nodeais an ancestor of nodecifcexists in the sub-tree rooted at nodea. Notice that 1 is trivially a common ancestor of any two nodes in the tree, but is not always thelowestcommon ancestor. For instance, the common ancestors of nodes 7 and 12 are 1 and 3, and 3 is the LCA since it is the node of greatest depth. The LCA of 2 and 13 is node 1, and the LCA of 5 and 11 is node 5. The definition of LCA guarantees that the LCA of any two nodes will always be unique.

The Problem:

Given two nodes in the tree using the numbering scheme shown above, determine the LCA of the two nodes.

完美二叉树是计算机科学家研究的最酷的结构之一。它们有很多特性,使它们非常适合使用。最好的属性之一是它们只能通过一个整数来描述树的深度。例如,forn=3 的完美二叉树看起来像:

一般来说,一个具有depthn的完美二叉树将恰好有2n+1-1个节点,并且可以按照上面看到的模式进行编号(当然还有其他方法来对树的节点进行编号,但这是我们将要使用的方案在这个问题中使用)。

处理树时出现的一个常见问题是查询树中两个节点的最低公共祖先(通常称为 LCA)。正式地,xandyi 的 LCA 是树中最大深度的节点,因此这是 xandy 的祖先。 Nodeai 是 nodecifc 的祖先,存在于以 nodea 为根的子树中。请注意,1 是树中任何两个节点的共同祖先,但并不总是最低的共同祖先。例如,节点 7 和 12 的共同祖先是 1 和 3,而 3 是 LCA,因为它是最大深度的节点。 2和13的LCA是节点1,5和11的LCA是节点5。LCA的定义保证了任意两个节点的LCA永远是唯一的。

问题:

使用上面显示的编号方案给定树中的两个节点,确定这两个节点的 LCA。

输入描述:

输入以一个正整数开始,T≤ 2∙106,表示测试用例的数量。 接下来是测试案例,每个案例都在单独的输入行上。 每个测试用例将包含两个空格分隔的整数 X 和 Y,以十六进制表示。 XandY 每个将包含来自集合 {0,1,2,3,4,5,6,7,8,9,a,b,c 的最多 1000 个字符 ,d,e,f},其中 a-frepresent 分别为 10-15。 您要确定 X 和 Y 的 LCA。 注:十六进制(基数为16)数dndn-1...d1d0通过以下公式转换为十进制数(基数为10):d0·160 +d1·161 +... +dn-1·16n-1 +dn ·16n。

输出描述:

对于每种情况,输出单行:

Case#x:y

其中xi是从1开始的案例编号,y是十六进制的LCA,没有前导0。 在每个测试用例的输出后留一个空行。

链接:https://ac.nowcoder.com/acm/contest/18480/J
来源:牛客网
 

输入

7
7 c
2 d
b 5
10 11
a020fac a030ccf
12afcdb 12afcdc
100000000 fffffffff

输出

Case #1: 3

Case #2: 1

Case #3: 5

Case #4: 8

Case #5: 501

Case #6: 255f9b

Case #7: 1

思路

众所周知,LCA需要遍历所有点确定其深度才能求出,但是在这个题里,这样做的成本非常大。因为这里每个数都是16进制数,而且还是高精数,这导致我们想要遍历整个完全树是不可能的。

因此,我苦思冥想,想到了一种可以不用转换进制也不需要遍历的方法。

我们先写一个函数比较X和Y的大小,然后将其中那个较大的数除以2,如果反复这样做,无论两个数是否在同一层,他们都会回到同一个节点(这个时候两数相等),这个节点就是他们的最大公共祖先。

代码量还是比较大的,毕竟是高精的LCA。

#include <iostream>
#include <cstring>
using namespace std;
 
char X[1001],Y[1001];
int x[1001],y[1001],l1,l2;
 
 
int power()// 比较两高精数的大小
{
    if(l1>l2) return 1;
    if(l1<l2) return -1;
    for(int i=l1;i>0;i--)
    {
        if(x[i]>y[i]) return 1;
        if(x[i]<y[i]) return -1;
    }
    return 0;
}
void change()
{
    int bo=1;
    while(bo!=0)//当X=Y时,说明此时两点都回到了他们的最大公共祖先处
    {
        bo=power();
        if(bo==1)//如果X>Y,就将X除以2
        {
            for(int i=l1;i>0;i--)
            {
                if(x[i]%2==1) x[i-1]+=16;
                x[i]/=2;
            }
            if(x[l1]==0) l1--;
        }
        if(bo==-1)//如果X<Y,就将Y除以2
        {
            for(int i=l2;i>0;i--)
            {
                if(y[i]%2==1) y[i-1]+=16;
                y[i]/=2;
            }
            if(y[l2]==0) l2--;
        }
    }
}
int main()
{
    int t;
    cin>>t;
    for(int i0=1;i0<=t;i0++)
    {
        scanf("%s%s",&X,&Y);
        l1=strlen(X);
        l2=strlen(Y);
        for(int i=0;i<l1;i++)
        {
            if(X[i]<='9'&&X[i]>='0')
            {
                x[l1-i]=X[i]-'0';
            }
            else
            {
                x[l1-i]=10+X[i]-'a';
            }
        }
        for(int i=0;i<l2;i++)
        {
            if(Y[i]<='9'&&Y[i]>='0')
            {
                y[l2-i]=Y[i]-'0';
            }
            else
            {
                y[l2-i]=10+Y[i]-'a';
            }
        }

        change();

        cout<<"Case #"<<i0<<": ";
        for(int i=l1;i>=1;i--)
        {
            if(x[i]>=10) cout<<char('a'+x[i]-10);
            else cout<<x[i];
        }
        if(i0<t) cout<<endl;
        cout<<endl;
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
    }
    return 0;
}

标签:Lowest,Case,16,题解,l1,tree,LCA,nodes,节点
来源: https://blog.csdn.net/m0_57811707/article/details/118768073