您的位置:首页 > 其它

Tarjan--LCA算法的个人理解即模板

2014-09-04 16:09 483 查看
tarjan---LCA算法的步骤是(当dfs到节点u时):

实际: 并查集+dfs

具体步骤:

1 在并查集中建立仅有u的集合,设置该集合的祖先为u

1 对u的每个孩子v:

1.1 tarjan之

1.2 合并v到父节点u的集合,确保集合的祖先是u

2 设置u为已遍历

3 处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=v所在的集合的祖先。

举例子:



假设遍历完10的孩子,要处理关于10的请求了

取根节点到当前正在遍历的节点的路径为关键路径,即1-3-8-10

集合的祖先便是关键路径上距离集合最近的点

比如此时:

1,2,5,6为一个集合,祖先为1,集合中点和10的LCA为1

3,7为一个集合,祖先为3,集合中点和10的LCA为3

8,9,11为一个集合,祖先为8,集合中点和10的LCA为8

10,12为一个集合,祖先为10,集合中点和10的LCA为10

你看,集合的祖先便是LCA吧,所以第3步是正确的

道理很简单,LCA(u,v)便是根至u的路径上到节点v最近的点

此段话语来自sre="http://purety.jp/akisame/oi/TJU/"

模板:

#include<iostream>
#include<vector>
using namespace std;

const int MAX=10001;
int father[MAX];
int rank[MAX];
int indegree[MAX];//保存每个节点的入度
int visit[MAX];
vector<int> tree[MAX],Qes[MAX];
int ancestor[MAX];

void init(int n)
{
for(int i=1;i<=n;i++)
{

rank[i]=1;
father[i]=i;
indegree[i]=0;
visit[i]=0;
ancestor[i]=0;
tree[i].clear();
Qes[i].clear();
}

}

int find(int n)
{
if(father
==n)
return n;
else
father
=find(father
);
return father
;
}//查找函数,并压缩路径

int Union(int x,int y)
{
int a=find(x);
int b=find(y);
if(a==b)
return 0;
//相等的话,x向y合并
else if(rank[a]<=rank[b])
{
father[a]=b;
rank[b]+=rank[a];
}
else
{
father[b]=a;
rank[a]+=rank[b];
}
return 1;

}//合并函数,如果属于同一分支则返回0,成功合并返回1

void LCA(int u)
{
ancestor[u]=u;
int size = tree[u].size();
for(int i=0;i<size;i++)
{
LCA(tree[u][i]);
Union(u,tree[u][i]);
ancestor[find(u)]=u;
}
visit[u]=1;
size = Qes[u].size();
for(int i=0;i<size;i++)
{
//如果已经访问了问题节点,就可以返回结果了.
if(visit[Qes[u][i]]==1)
{
cout<<ancestor[find(Qes[u][i])]<<endl;
return;
}
}
}

int main()
{
int cnt;
int n;
cin>>cnt;
while(cnt--)
{
cin>>n;;
init(n);
int s,t;
for(int i=1;i<n;i++)
{
cin>>s>>t;
tree[s].push_back(t);
indegree[t]++;
}
//这里可以输入多组询问
cin>>s>>t;
//相当于询问两次
Qes[s].push_back(t);
Qes[t].push_back(s);
for(int i=1;i<=n;i++)
{
//寻找根节点
if(indegree[i]==0)
{
LCA(i);
break;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: