//lca(求最小公共祖先) 此处考虑使用最简单的被增法解决最近公共祖先问题
#include<stdio.h>
#include<string.h>
struct edge
{
int to,next;
}edge[1000];
int cnt,head[1000],lg[1000];
void add(int x,int y)//链式前向星存边,即存图
{
edge[++cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
int deep[1000],fa[1000][21];//用deep数组存储每个点的深度,而用fa数组存储每个数,从i出发走2^j步之后到达的地方
void dfs(int now,int father)
{
int i;
fa[now][0]=father;//从now出发,走2^0步,即走1步,就到达的是其父亲节点
deep[now]=deep[father]+1;
for(i=1;(1<<i)<=deep[now];i++)//1<<i表示把1化为二进制,然后向左移动i位,即表示2^i.同时应注意到,fa数组存的是从now开始跳2^j层后的点位置,那么,你跳的层数一定是小于当前点的深度的,所以才有中间那句判断条件
{
fa[now][i]=fa[fa[now][i-1]][i-1];//因为2^j可以表示为2^j-1+2^j-1,也就是两个相加的形式,因为最初那个fa[now][0]是赋了值的,就可以往后推,得到后面数组元素的值
}
for(i=head[now];i!=0;i=edge[i].next)//链式前向星向后摸索,因为初始化的时候赋值head[i]为0,所以判断结束的时候以0为标准
{
if(edge[i].to!=father)//跳过父亲节点,不然就回不去了
dfs(edge[i].to,now);
}
}
void swap(int *x,int *y)
{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
int lca(int x,int y)
{
int k;
if(deep[x]<deep[y])//避免分情况讨论,所以交换,让x的深度永远大于y的深度
swap(&x,&y);
while(deep[x]>deep[y])//将x和y提到同一高度
{
x=fa[x][lg[deep[x]-deep[y]-1]];
}
if(x==y)//跳到一个点后,两者相遇,即可判断该点为最小祖先节点
return x;
else
{
for(k=lg[deep[x]-1];k>=0;k--)
{
if(fa[x][k]!=fa[y][k])
{
y=fa[y][k];
x=fa[x][k];
}
}
return fa[x][0];//2^0表示为1,即其上一个节点,即其父亲节点
}
}
int main(void)
{
int i,j,t,n,m,s,x,y,a,b;
scanf("%d %d %d",&n,&m,&s);
for(i=1;i<=n;i++)
{
scanf("%d %d",&x,&y);
add(x,y);
add(y,x);//注意此处是双向存入,也就意味着,既以x为起点,以y为终点;也以y为起点,以x为终点。但是这里的双向存入应该理解为有两个表,并不意味着两个表合在一起
}
dfs(s,0);//s表示的是根节点的值,跟节点没有父亲节点,所以传入其父亲节点实参为0
for(i=1;i<=n;i++)
{
lg[i]=lg[i-1]+(1<<lg[i-1]==i);//存储每个数字的lg值,因为lg1=0.所以要加1;
}
while(m--)
{
scanf("%d %d",&a,&b);//查询a,b的最小公共祖先
printf("%d\n",lca(a,b));
}
}
acm算法之lca最近公共祖先
最新推荐文章于 2023-07-21 15:29:56 发布