现在的位置: 首页 > 算法 > 正文

poj 1470 || zoj 1141 Closest Common Ancestors (LCA)

2018年09月23日 算法 ⁄ 共 1775字 ⁄ 字号 评论关闭

题目链接:   poj 1470

题目大意:   给出一棵树,然后有有限次查询(a,b)

                  每次查询节点a与节点b的最近公共祖先

                  输出节点作为最近公共祖先的次数

解题思路:   离线查询最近公共祖先

                  把每次查到的结果visit[ ]++,最后遍历一遍就行了

代码:

//Final   LCA离线算法求最近公共祖先
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <vector>
using namespace std;
#define MAX 1000
vector<int> Hash[MAX],Qes[MAX];
int n,ansetor[MAX],visit[MAX],parent[MAX],fathernum[MAX],num[MAX];

void Init(int n)      //并查集初始化
{
    for(int i=1;i<=n;i++)
        parent[i]=i;
}

int Find(int x)        //并查集查找和压缩路径
{
    int s,j;
    s=x;
    while(x!=parent[x])
        x=parent[x];
    while(x!=s)
    {
        j=parent[s];
        parent[s]=x;
        s=j;
    }
    return x;
}

void Union(int r1,int r2)   //并查集合并
{
    int R1,R2;
    R1=Find(r1);
    R2=Find(r2);
    if(R1!=R2)
        parent[R1]=R2;
}

void LCA(int u)
{
    int i,size;
    size=Hash[u].size();
    ansetor[u]=u;
    for(i=0;i<size;i++)     //size()从0开始计算
    {
        LCA(Hash[u][i]);
        Union(u,Hash[u][i]);
        ansetor[Find(u)]=u;    //***可以是Find(u)或者Find(Hash[u][i]),因为已经合并了
    }
    visit[u]=1;
    size=Qes[u].size();
    for(i=0;i<size;i++)         //size()从0开始计算
    {
        if(visit[Qes[u][i]])    //如果需要查找的两个点其中一个点之前被访问过,
		{	                    //那么此时它的祖先就是它们的最近公共祖先
            num[ansetor[Find(Qes[u][i])]]++;   //***只能是Find(Qes[u][i]),因为此时u和Qes[u][i]并未合并
        }
    }
}

int main()
{
    int i,j,a,b,c,m,k=0;
    char ch,ch1;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            Hash[i].clear();
            Qes[i].clear();
        }
        k=0;
        Init(n);
        memset(visit,0,sizeof(visit));
        memset(ansetor,0,sizeof(ansetor));
        memset(num,0,sizeof(num));
        memset(fathernum,0,sizeof(fathernum));
        while(1)
        {
            scanf("%d%c",&a,&ch);
            if(ch!=':')
            {
                m=a;
                break;
            }
            scanf("%c%d%c",&ch1,&b,&ch1);
            for(j=1;j<=b;j++)
            {
                scanf("%d",&c);
                Hash[a].push_back(c);  //表示a是b的父亲
                fathernum[c]++;        //记录每个顶点父亲的个数
            }
            getchar();
        }
        while(scanf("%c",&ch)!=EOF)
        {
            if(ch=='(')
            {
                scanf("%d%d",&a,&b);
                getchar();
                k++;
                Qes[a].push_back(b);   //需要查找的两点
                Qes[b].push_back(a);   //需要查找的两点
                if(k>=m)
                {
                    break;
                }
            }
        }
        for(i=1;i<=n;i++)
        {
            if(!fathernum[i])          //没有父亲结点的点既是整棵树的根节点
            {
                LCA(i);
                break;
            }
        }
        for(i=1;i<=n;i++)
        {
            if(num[i])
               printf("%d:%d\n",i,num[i]);
        }
     }
    return 0;
}

抱歉!评论已关闭.