• POJ 1330 Nearest Common Ancestors(求最近的公共祖先)


    题意:给出一棵树,再给出两个节点a、b,求离它们最近的公共祖先。
    方法一:

      先用vector存储某节点的子节点,fa数组存储某节点的父节点,最后找出fa[root]=0的根节点root。
          之后求每个节点的“高度”,更节点的高度为1,每往下一层高度+1。
          读取a和b后,先求出它们位于同一个高度的祖先:
          1.若此祖先相同,则即为最近的公共祖先。
          2.若不相同,则求各自的父节点,知道两者的父节点相同,即为最近的公共祖先。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <vector>
    
    using namespace std;
    const int maxn=10010;
    int n;
    vector<int> son[maxn];
    int depth[maxn];  //depth[i]表示节点i的“高度”,这里根节点高度为1,每往下一层高度+1
    int fa[maxn];  //fa[i]表示i的父节点
    
    void init(){
        for(int i=0;i<maxn;i++)
            son[i].clear();
        memset(depth,0,sizeof(depth));
        memset(fa,0,sizeof(fa));
    }
    //求出每个节点的“高度”
    void dealdepth(int u){
        for(int i=0;i<son[u].size();i++){
            int v=son[u][i];
            depth[v]=depth[u]+1;
            dealdepth(v);
        }
        return;
    }
    
    int solve(int a,int b){
        //depth[a]>depth[b],表示b在a的上方,先求出a的与b在同一层的祖先
        if(depth[a]>depth[b]){
            while(depth[a]>depth[b])
                a=fa[a];
            if(a==b)
                return a;
        }
        //depth[a]<depth[b],表示a在b的上方,先求出b的与a在同一层的祖先
        else if(depth[a]<depth[b]){
            while(depth[a]<depth[b])
                b=fa[b];
            if(a==b)
                return a;
        }
        //同时求祖先,直至相等
        while(a!=b){
            a=fa[a];
            b=fa[b];
        }
        return a;
    }
    int main()
    {
        int t,a,b,root,u,v;  //root:根节点
        scanf("%d",&t);
        while(t--){
            init();
            scanf("%d",&n);
            for(int i=1;i<n;i++){
                scanf("%d%d",&u,&v);
                son[u].push_back(v);
                fa[v]=u;
            }
            for(int i=1;i<=n;i++){
                if(fa[i]==0){
                    root=i;
                    break;
                }
            }
            depth[root]=1;
            dealdepth(root);
            scanf("%d%d",&a,&b);
            int ans=solve(a,b);
            printf("%d
    ",ans);
        }
        return 0;
    }

    方法二:用Tarjan算法求

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <string>
    #include <vector>
    /*
    Tarjan算法
    */
    using namespace std;
    const int maxn=10001;
    int n,t,a,b;  //a,b是要求公共祖先的两点
    bool flag;  //flag为true表明LCA已经求出了a,b的公共祖先,那么就不用继续往下求了
    int vis[maxn];  //LCA标记哪些节点已被访问过
    int indegree[maxn];  //记录每个节点的入度,用来找寻根节点
    int head[maxn];
    int tot;
    int ans;
    
    struct Edge{
        int to,next;
    }edge[maxn];
    
    void add(int u,int v){
        edge[tot].next=head[u];
        edge[tot].to=v;
        head[u]=tot++;
    }
    //并查集
    struct UF{
        int fa[maxn];
        void init(){
            for(int i=0;i<=n;i++)
                fa[i]=i;
        }
        int find_root(int x){
            if(fa[x]!=x)
                fa[x]=find_root(fa[x]);
            return fa[x];
        }
        void Union(int u,int v){
            fa[v]=fa[u];
        }
    }uf;
    
    void LCA(int u){
        if(flag)
            return;
        int v;
        for(int k=head[u];k!=-1;k=edge[k].next){
            v=edge[k].to;
            LCA(v);
            uf.Union(u,v);
        }
        vis[u]=1;  //标记节点u
        //看看u是否是查询的两个节点中的一个
        if(u==a){
            if(vis[b]){
                ans=uf.fa[uf.find_root(b)];
                flag=true;
            }
        }
        else if(u==b){
            if(vis[a]){
                ans=uf.fa[uf.find_root(a)];
                flag=true;
            }
        }
    }
    int main()
    {
        int root,u,v;
        cin>>t;
        while(t--){
            memset(indegree,0,sizeof(indegree));
            memset(head,-1,sizeof(head));
            tot=0;
            cin>>n;
            for(int i=1;i<n;i++){
                cin>>u>>v;
                add(u,v);
                indegree[v]++;
            }
            for(int i=1;i<=n;i++){
                if(!indegree[i]){
                    root=i;
                    break;
                }
            }
            cin>>a>>b;
            flag=false;
            memset(vis,0,sizeof(vis));
            uf.init();//一开始忘记初始化了额
            LCA(root);
            cout<<ans<<endl;
    
        }
        return 0;
    }
  • 相关阅读:
    java 排序算法
    简单选择排序
    .net 调用css/javascipt出现的问题及解决
    《大道至简》的幕后故事(4):“愚公移山记”军事地理篇
    asp.net web services
    《大道至简》的幕后故事:终结篇、勘误和PDF下载
    Java开发人员的十大戒律
    NUnit 使用
    快速排序
    .NET中回发或回调参数无效问题的解决
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3343623.html
Copyright © 2020-2023  润新知