http://poetrinity.diandian.com/post/2012-02-04/19684915
LCA(最近公共祖先)问题
LCA问题便是指,给定一棵树T和两个节点u和v,找出u和v的离根节点最远的公共祖先。
方法一:Tarjan离线算法
在学习离线算法的时候先需要先巩固一下深度搜索,并查集
Tarjan离线算法是基于深度优先搜索的,我们从根开始向下搜索,搜到一个节点的时候首先判断该节点所有子节点是否访问过,如果都已经访问过,则判断该节点是否询问点里面的其中一个,如果是,则判断跟它相对应的那个点是否已经访问过,如果访问过,则他们的最近公共祖先便是已经访问过的那个节点的当前节点,如果另外一个节点没有访问,则继续进行深度搜索。
例题:POJ 1330 Nearest Common Ancestors
这道题目是让求解最近公共祖先的问题,不过这道题目让我们求解的只有一组,对于离线算法而言,可以一下子求解多组,只要我们把需要求解的所有组建立一张表,当搜索到这个节点的时候去寻找跟这个节点相连的节点有没有被访问过的,如果访问过,则将他们的公共祖先先记录下来,这样到最后搜索完所有的点后就可以在O(1)的时间内输出所有需要求解最近公共祖先的解了。
方法二:在线算法
第二种求解最近公共祖先就是采用在线算法,这种算法可以融入ST算法来加快速度,在这儿先以一种简单的方法来对在线算法有个初步的认识。
在线算法首先也是给给出的边来建立一个图,找出根节点,然后深度搜索每一个节点,求出每一个节点的深度(根节点深度可以赋值为0),然后我们找到要求的那对点的深度,如果两者深度不一样,先让深度大的网上移动,直到两者深度一样,此时判断两者是否为同一个节点,如果不是同一个节点,就都将他们改变为他们的父节点,这样一步一步往上移动,最后当遇到两者一样的时候,这个点就是它们的根节点了。
同样是上面一道例题,在线算法的代码如下
采取上面一步一步往上推的在线算法是比较山寨的,时间可能比较慢点儿,所以我们就可以采用ST算法来对上面的方法进行一些预处理。当然有不好的地方必然也有点儿小优点。问题:现在给一个树添加一条边,你能求出这个树里面所新形成的环吗?输出这个环的各条边。对于这个问题我们就可以采用上面一步一步的向上搜索的方法,这样最后就能输出这个环了。当然要按问题讨论的,我们还是应该学一下ST算法来预处理下加速的。
方法一:Tarjan离线算法
在学习离线算法的时候先需要先巩固一下深度搜索,并查集
Tarjan离线算法是基于深度优先搜索的,我们从根开始向下搜索,搜到一个节点的时候首先判断该节点所有子节点是否访问过,如果都已经访问过,则判断该节点是否询问点里面的其中一个,如果是,则判断跟它相对应的那个点是否已经访问过,如果访问过,则他们的最近公共祖先便是已经访问过的那个节点的当前节点,如果另外一个节点没有访问,则继续进行深度搜索。
例题:POJ 1330 Nearest Common Ancestors
这道题目是让求解最近公共祖先的问题,不过这道题目让我们求解的只有一组,对于离线算法而言,可以一下子求解多组,只要我们把需要求解的所有组建立一张表,当搜索到这个节点的时候去寻找跟这个节点相连的节点有没有被访问过的,如果访问过,则将他们的公共祖先先记录下来,这样到最后搜索完所有的点后就可以在O(1)的时间内输出所有需要求解最近公共祖先的解了。
#include
#include
using namespace std;
const int size=10005;
int T,N;
vectorvec[size];
int father[size];
bool root[size];
bool visited[size];
int first,second;
void In_data()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)
{
vec[i].clear();
father[i]=i;
root[i]=true;
visited[i]=false;
}
for(int i=1;i {
int beg,end; scanf("%d%d",&beg,&end);
vec[beg].push_back(end);
root[end]=false;
}
scanf("%d%d",&first,&second); //记录两个节点
}
int Find(int x)
{
if(father[x]==x)
return x;
else
return father[x]=Find(father[x]);
}
void Union(int a,int b)
{
int A=Find(a);
int B=Find(b);
if(A!=B)
father[B]=A;
}
void LCA(int parent)
{
for(int i=0;i {
LCA(vec[parent][i]);
Union(parent,vec[parent][i]);
}
visited[parent]=true; //确认该点已经搜索过
//如果目前的访问的节点就是其中一个节点,查看两外一个是否访问过
if(parent==first&&visited[second]==true)
{
printf("%d\n",Find(second));
return ;
}
else if(parent==second&&visited[first]==true)
{
printf("%d\n",Find(first));
return ;
}
}
int main()
{
//freopen("1.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
In_data();
//以根节点为入口
for(int i=1;i<=N;++i)
{
if(root[i]==true)
{
LCA(i);
break;
}
}
}
}
方法二:在线算法
第二种求解最近公共祖先就是采用在线算法,这种算法可以融入ST算法来加快速度,在这儿先以一种简单的方法来对在线算法有个初步的认识。
在线算法首先也是给给出的边来建立一个图,找出根节点,然后深度搜索每一个节点,求出每一个节点的深度(根节点深度可以赋值为0),然后我们找到要求的那对点的深度,如果两者深度不一样,先让深度大的网上移动,直到两者深度一样,此时判断两者是否为同一个节点,如果不是同一个节点,就都将他们改变为他们的父节点,这样一步一步往上移动,最后当遇到两者一样的时候,这个点就是它们的根节点了。
同样是上面一道例题,在线算法的代码如下
#include
#include
using namespace std;
const int size=10005;
int T,N,first,second;
vectorvec[size]; //用来记录图的信息
bool root[size]; //用来查找根节点
int depth[size]; //用来记录该节点的深度
int father[size]; //用来记录该节点的父节点
//录入数据
void In_data()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)
{
vec[i].clear();
depth[i]=0;
root[i]=true;
father[i]=i;
}
for(int i=1;i {
int beg,end; scanf("%d%d",&beg,&end);
vec[beg].push_back(end);
father[end]=beg;
root[end]=false;
}
scanf("%d%d",&first,&second);
}
//parent表示根节点,dep表示当前节点的深度
void depth_plus(int parent,int dep)
{
for(int i=0;i {
depth_plus(vec[parent][i],dep+1);
}
depth[parent]=dep;
}
//在线算法查找最近公共祖先
int find_ancestor()
{
if(depth[first]>depth[second])
{
while(depth[first]!=depth[second])
{
first=father[first];
}
}
else if(depth[second]>depth[first])
{
while(depth[first]!=depth[second])
{
second=father[second];
}
}
while(first!=second)
{
first=father[first];
second=father[second];
}
return first;
}
int main()
{
//freopen("1.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
In_data();
//以根节点为入口,给每个点赋予深度
for(int i=1;i<=N;++i)
{
if(root[i]==true)
{
depth_plus(i,0);
break;
}
}
printf("%d\n",find_ancestor());
}
}
采取上面一步一步往上推的在线算法是比较山寨的,时间可能比较慢点儿,所以我们就可以采用ST算法来对上面的方法进行一些预处理。当然有不好的地方必然也有点儿小优点。问题:现在给一个树添加一条边,你能求出这个树里面所新形成的环吗?输出这个环的各条边。对于这个问题我们就可以采用上面一步一步的向上搜索的方法,这样最后就能输出这个环了。当然要按问题讨论的,我们还是应该学一下ST算法来预处理下加速的。

本文介绍了两种求解最近公共祖先(LCA)问题的方法:Tarjan离线算法和在线算法。Tarjan离线算法基于深度优先搜索,可以一次性求解多组查询;在线算法则通过调整节点深度逐步上溯找到最近公共祖先。
1007

被折叠的 条评论
为什么被折叠?



