Poj1330
题意:求一棵树中两点的最近祖先。
思路:直接用tarjan算法。(还可以求出强连通分量)
下面是我对这个算法的理解,假如我们是求u,v的最近公共祖先。
Tarjan主要利用了dfs的性质。我们在进行dfs时,如果我们已经遍历了u,当我们遍历v时,这时他们最近的公共祖先也在栈里边。这时间我们可以在设一个f[i]数组,这个数组是用来确定i的祖先,我们在遍历的时间可以对f进行控制,利用并查集找的答案。
//最近公共祖先tarjan算法
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
using namespace std;
const int MAX = 10010;
vector<int> edge[MAX];
vector<int> query[MAX];//查询记录与u节点连接的的v
int f[MAX],n,indegree[MAX];
int lca[MAX],vis[MAX];
int findx(int x)
{
int r = x;int i,j;
while (f[r] != r) //循环结束,则找到根节点
r = f[r];
i = x;
while (i != r) //本循环修改查找路径中所有节点
{
j = f[i];
f[i] = r;
i = j;
}
return r;
}
void Union(int x,int y)
{
int fx,fy;
fx = findx(x);
fy = findx(y);
if(fx <= fy)
f[fy] = fx;
else f[fx]=fy;
}
void dfs(int u)
{
int i;
lca[u]=u;
for(i=0;i<edge[u].size();i++)
{
//cout<<u<<endl;
dfs(edge[u][i]);//以下为回溯部分
// cout<<edge[u][i]<<endl;
Union(u,edge[u][i]);
lca[findx (u)]=u;
}
vis[u]=1;
for(i=0;i<query[u].size();i++)//遍历过则说明已找到答案
{
if(vis[query[u][i]])
{
printf("%d\n",lca[findx (query[u][i])]);
return ;
}
}
}
void init()
{
int i;
for(i=0;i<=n;i++)
{
edge[i].clear();
query[i].clear();
indegree[i]=0;vis[i]=0;
f[i]=i;lca[i]=0;
}
}
int main()
{
int t,i,a,b;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
indegree[b]++;
edge[a].push_back(b);
}
scanf("%d%d",&a,&b);
query[a].push_back(b);
query[b].push_back(a);
for(i=1;i<=n;i++)
{
if(!indegree[i])
{
dfs(i);
break;
}
}
}
return 0;
}