zoukankan      html  css  js  c++  java
  • POJ 1330 Nearest Common Ancestors (LCA入门题)

    Nearest Common Ancestors
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 12478   Accepted: 6652

    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

    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
    
    

    Source

     
     
     
    很简单的LCA题目。
    总结和检测下模板之用。
    LCA有离线算法和在线算法。
    程序如下。
     
     
    在线算法:
    /*
    POJ 1330
    求最近公共祖先(LCA)
    
    在线算法 DFS+ST描述
    
    对于本题只有一个查询,不适合用在线算法,用离线算法效率更高
    
    */
    
    #include<stdio.h>
    #include<iostream>
    #include<math.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=10010;
    
    int rmq[2*MAXN];//建立RMQ的数组
    
    //***************************
    //ST算法,里面含有初始化init(n)和query(s,t)函数
    //点的编号从1开始,1-n.返回最小值的下标
    //***************************
    struct ST
    {
        int mm[2*MAXN];//mm[i]表示i的最高位,mm[1]=0,mm[2]=1,mm[3]=1,mm[4]=2
        int dp[MAXN*2][20];
        void init(int n)
        {
            mm[0]=-1;
            for(int i=1;i<=n;i++)
            {
                mm[i]=((i&(i-1))==0?mm[i-1]+1:mm[i-1]);
                dp[i][0]=i;
            }
            for(int j=1;j<=mm[n];j++)
              for(int i=1;i+(1<<j)-1<=n;i++)
                 dp[i][j]=rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
        }
        int query(int a,int b)//查询a到b间最小值的下标
        {
            if(a>b)swap(a,b);
            int k=mm[b-a+1];
            return rmq[dp[a][k]]<rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
        }
    };
    
    //边的结构体定义
    struct Node
    {
        int to,next;
    };
    
    /* ******************************************
    LCA转化为RMQ的问题
    MAXN为最大结点数。ST的数组 和 F,edge要设置为2*MAXN
    
    F是欧拉序列,rmq是深度序列,P是某点在F中第一次出现的下标
    *********************************************/
    
    struct LCA2RMQ
    {
        int n;//结点个数
        Node edge[2*MAXN];//树的边,因为是建无向边,所以是两倍
        int tol;//边的计数
        int head[MAXN];//头结点
    
        bool vis[MAXN];//访问标记
        int F[2*MAXN];//F是欧拉序列,就是DFS遍历的顺序
        int P[MAXN];//某点在F中第一次出现的位置
        int cnt;
    
        ST st;
        void init(int n)//n为所以点的总个数,可以从0开始,也可以从1开始
        {
            this->n=n;
            tol=0;
            memset(head,-1,sizeof(head));
        }
        void addedge(int a,int b)//加边
        {
            edge[tol].to=b;
            edge[tol].next=head[a];
            head[a]=tol++;
            edge[tol].to=a;
            edge[tol].next=head[b];
            head[b]=tol++;
        }
    
        int query(int a,int b)//传入两个节点,返回他们的LCA编号
        {
            return F[st.query(P[a],P[b])];
        }
    
        void dfs(int a,int lev)
        {
            vis[a]=true;
            ++cnt;//先加,保证F序列和rmq序列从1开始
            F[cnt]=a;//欧拉序列,编号从1开始,共2*n-1个元素
            rmq[cnt]=lev;//rmq数组是深度序列
            P[a]=cnt;
            for(int i=head[a];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                if(vis[v])continue;
                dfs(v,lev+1);
                ++cnt;
                F[cnt]=a;
                rmq[cnt]=lev;
            }
        }
    
        void solve(int root)
        {
            memset(vis,false,sizeof(vis));
            cnt=0;
            dfs(root,0);
            st.init(2*n-1);
        }
    }lca;
    
    bool flag[MAXN];
    
    int main()
    {
        int T;
        int N;
        int u,v;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&N);
            memset(flag,false,sizeof(flag));
            lca.init(N);
            for(int i=1;i<N;i++)
            {
                scanf("%d%d",&u,&v);
                lca.addedge(u,v);
                flag[v]=true;
            }
            int root;
            for(int i=1;i<=N;i++)
              if(!flag[i])
              {
                  root=i;
                  break;
              }
            lca.solve(root);
            scanf("%d%d",&u,&v);
            printf("%d\n",lca.query(u,v));
        }
        return 0;
    }

    离线算法。

    /*
    POJ 1330
    离线算法
    
    */
    
    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    using namespace std;
    
    const int MAXN=10010;
    
    int F[MAXN];//并查集
    int r[MAXN];//并查集中集合的个数
    bool vis[MAXN];//访问标记
    int ancestor[MAXN];//祖先
    struct Node
    {
        int to,next;
    }edge[MAXN*2];
    
    int head[MAXN];
    int tol;
    void addedge(int a,int b)
    {
        edge[tol].to=b;
        edge[tol].next=head[a];
        head[a]=tol++;
        edge[tol].to=a;
        edge[tol].next=head[b];
        head[b]=tol++;
    }
    
    
    struct Query
    {
        int q,next;
        int index;//查询编号
    }query[MAXN*2];//查询数
    int answer[MAXN*2];//查询结果
    int cnt;
    int h[MAXN];
    int tt;
    int Q;//查询个数
    
    
    void add_query(int a,int b,int i)
    {
        query[tt].q=b;
        query[tt].next=h[a];
        query[tt].index=i;
        h[a]=tt++;
        query[tt].q=a;
        query[tt].next=h[b];
        query[tt].index=i;
        h[b]=tt++;
    }
    
    void init(int n)
    {
        for(int i=1;i<=n;i++)
        {
            F[i]=-1;
            r[i]=1;
            vis[i]=false;
            ancestor[i]=0;
            tol=0;
            tt=0;
            cnt=0;//已经查询到的个数
        }
        memset(head,-1,sizeof(head));
        memset(h,-1,sizeof(h));
    }
    int find(int x)
    {
        if(F[x]==-1)return x;
        return F[x]=find(F[x]);
    }
    
    void Union(int x,int y)//合并
    {
        int t1=find(x);
        int t2=find(y);
        if(t1!=t2)
        {
            if(r[t1]<=r[t2])
            {
                F[t1]=t2;
                r[t2]+=r[t1];
            }
            else
            {
                F[t2]=t1;
                r[t1]+=r[t2];
            }
        }
    }
    
    
    void LCA(int u)
    {
        //if(cnt>=Q)return;//不要加这个
        ancestor[u]=u;
        vis[u]=true;//这个一定要放在前面
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(vis[v])continue;
            LCA(v);
            Union(u,v);
            ancestor[find(u)]=u;
        }
        for(int i=h[u];i!=-1;i=query[i].next)
        {
            int v=query[i].q;
            if(vis[v])
            {
                answer[query[i].index]=ancestor[find(v)];
                cnt++;//已经找到的答案数
            }
        }
    }
    bool flag[MAXN];
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int T;
        int N;
        int u,v;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&N);
            init(N);
            memset(flag,false,sizeof(flag));
            for(int i=1;i<N;i++)
            {
                scanf("%d%d",&u,&v);
                flag[v]=true;
                addedge(u,v);
            }
            Q=1;//查询只有一组
            scanf("%d%d",&u,&v);
            add_query(u,v,0);//增加一组查询
            int root;
            for(int i=1;i<=N;i++)
              if(!flag[i])
              {
                  root=i;
                  break;
              }
            LCA(root);
            for(int i=0;i<Q;i++)//输出所有答案
              printf("%d\n",answer[i]);
        }
        return 0;
    }
  • 相关阅读:
    线程的生命周期
    同步与死锁
    同步与死锁
    线程的休眠
    线程的休眠
    RTSP转RTMP、FLV、HLS网页无插件视频直播-LiveNVR功能介绍-音频开启
    使用LiveNVR实现将RTSP转RTMP、FLV、HLS,实现监控摄像头无插件直播
    使用LiveNVR实现安防摄像头RTSP流WEB无插件播放的延迟调优
    Javascript之数据执行原理探究
    Javascript之动画1
  • 原文地址:https://www.cnblogs.com/kuangbin/p/2697395.html
Copyright © 2011-2022 走看看