POJ1330 Nearest Common Ancestors

本文介绍了一种利用深度优先搜索(DFS)结合并查集的数据结构解决最近公共祖先(LCA)问题的方法。这种方法避免了使用复杂的Tarjan算法,并通过简单的DFS遍历与并查集合并操作,确保了树形结构的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

方法:dfs+并查集

LCA问题一般是用Tarjan算法来解决,其实对于这道题而言的话只需要用dfs+并查集就可以了。用一句话说就是在深度优先遍历树中节点u的时候,当遍历完u的每个孩子后将u和孩子节点进行合并(union)。

合并的时候需要注意的是不能用并查集的rank优化,因为这里在合并的同时仍要保存原树中的结构。比如,如果对于题目中的第二个用例

        2

           \

           3

        /      \

      4        1

                /

              5

要是按照以下代码合并:

void unions(int a,int b)
{
	int r1=find(a);
	int r2=find(b);
	if(rank[r1]>rank[r2])
	f[r2]=r1;
	else
	{
		f[r1]=r2;
		if(rank[r2]==rank[r1])
		rank[r2]++;
	}
}
最先合并的是3和4,而且根据上面的代码合并后4会是3的父亲,5是1的父亲,当回溯到3合并3和1的时候,1又会是3的父亲。这样就打乱了原来的结构,答案也就错了。所以在unions(a,b)中直接令f[b]=a即可。当然如果按照上面代码合并的话,另外记录父亲节点也是可以的,但会比较麻烦。

#include <stdio.h>
#include <memory.h>

#define N 10001

int tree[N][101];
int visit[N];
int f[N];
int into[N];
int a,b;

int find(int x)
{
	while(x!=f[x])
		x=f[x];
	return x;
}
//为了保持原来树中的结构和并查集中的一致
//不能使用rank数组,直接f[b]=a即可
//因为深度遍历时,若要unions(a,b),a是b的父亲
void unions(int a,int b)
{
	f[b]=a;
}
void lca(int u)
{
	f[u]=u;
	for(int i=1;i<=tree[u][0];i++)
	{
		lca(tree[u][i]);
		unions(u,tree[u][i]);
	}
	visit[u]=1;
	if(u==a&&visit[b])
		printf("%d\n",f[find(b)]);
	else if(u==b&&visit[a])
		printf("%d\n",f[find(a)]);
	return;
}
int main()
{
	int t,n,u,v;
	scanf("%d",&t);
	while(t--)
	{
		memset(tree,0,sizeof(tree));
		memset(visit,0,sizeof(visit));
		memset(into,0,sizeof(into));
		memset(f,0,sizeof(f));
		scanf("%d",&n);
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d",&u,&v);
			tree[u][++tree[u][0]]=v;
			into[v]++;
		}
		scanf("%d%d",&a,&b);
		for(int i=1;i<=n;i++)
			if(!into[i])
			{
				lca(i);
				break;
			}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值