【LCA-Tarjan】POJ 1330 Nearest Common Ancestors

前言

本人第一次做LCA,因为这是一个模板题,所以只讲算法思想

链接

http://poj.org/problem?id=1330

大意

在一棵树上,有 n n 个节点,n1条边,给定两个节点 p,q p , q ,求它们的最近公共祖先

思路

Tarjian T a r j i a n 算法(注意不是求强联通分量的那个 Tarjian T a r j i a n ,而是这个人很厉害,发明了许多算法,所以就用他的名字命名)
核心思想:并查集+ dfs d f s
dfs d f s 的过程中,用并查集保存祖先,当发现两个节点都被找到时,就输出先找到的那个节点的祖先。
原理是因为 dfs d f s 是深度优先,所以它先找到的一定深度更浅

代码

#include<cstdio>
#include<cstring>
#define N 10005
#define zero(a) memset(a,0,sizeof(a))//清0函数
using namespace std;bool ok;
int l[N],n,t,tot,p,q,root,f[N];
bool vis[N];
struct node{int from,next,to;}edge[N];//邻接表
void add(int u,int v){edge[++tot].from=u;edge[tot].to=v;edge[tot].next=l[u];l[u]=tot;}//建边
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}//并查集查询
void LRZ()//输入
{
    zero(vis);zero(l);//初始化
    scanf("%d",&n);int x,y;tot=0;//初始化,记住一个都不能少,要不然会CE或者MLE
    for(int i=1;i<n;i++) {scanf("%d%d",&x,&y);add(x,y);vis[y]=true;}//因为这是一个有向图,所以我们要找到一个入度为0的节点作为根节点
    scanf("%d%d",&p,&q);//输入
    for(int i=1;i<=n;i++) 
    if(!vis[i]) //入队为0
    {root=i;return;}//它就是根节点,赋值,结束
}
void dfs(int k)//dfs
{
    if(ok) return;//已经找到直接退出
    vis[k]=true;//标记已经走过
    if(k==p&&vis[q]) {printf("%d\n",find(q));ok=true;return;}//找到两个就输出先找到的那个的祖宗
    if(k==q&&vis[p]) {printf("%d\n",find(p));ok=true;return;}//记得标记
    for(int i=l[k];i;i=edge[i].next)//邻接表
    {
        dfs(edge[i].to);//递归
        if(ok) return;//若找到了直接退出
        f[find(edge[i].to)]=edge[i].from;//赋值
    }
    return;
}
void tarjan()//tarjan算法
{
    for(int i=1;i<=n;i++) f[i]=i;//并查集初始化
    zero(vis);ok=false;//初始化
    dfs(root);//搜
    return;//返回
}
int main()
{
    scanf("%d",&t);//输入
    while(t--)
    {
        LRZ();//输入
        tarjan();//计算
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值