最近公共祖先(LCA)POJNearest Common Ancestors 1330


最近公共祖先(简称LCA(Lowest Common Ancestor)):官方定义是对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。

其实就是一棵树上两个点,这两点公共的离这两个点最近的公共节点。就好比下图:

最近公共祖先(LCA)POJNearest Common Ancestors 1330_第1张图片

在这个图中,千万别把根节点搞错! 乍一看,好像是8,那就等着wa吧。仔细看一下,图其实根节点是9。

例如,6,12的公共节点为4,8,5,9,最近公共祖先为4。

这里讲一下如何使用倍增法求最小公共祖先。

核心思路:

  1. init()初始化的构建,需要多次用到2的i次方,因此需要建立一个bit[]数组存储一下2的i次方。i一般小小于30.
  2. father[][]数据的构建。所谓倍增,就是以2的次方进行寻找某个节点的祖先节点。在图中,12的2^0节点为16,2^1为10,2^2为4,这样以2的次方进行查找,这个过程是利用dfs实现,把每个点的第2的i次方个节点存放在数组里。father[i][j]=i节点的第2^j节点。
  3. LAC函数的构建。做完了步骤一的工作,就进入寻找最近公共祖先。首先判断a,b两个节点的深度差(深度就是距离根节点之间有几个节点,包括自己本身),再将二者提升到相近深度,这里也是利用倍增的方法。

核心代码:

​
​
const int N=1e6+10;
long long bit[N];//用于存储2次方
int depth[N];//存储节点的深度
int fa[N][30];//fa[i][j]用于存储i上2的j次方的点 
vectore[N];
int n,m,s;
void init(){//求出2的各个次方 
	bit[0]=1;
	for(int i=1;i<=29;i++)
	bit[i]=(bit[i-1])*2;
} 
void dfs(int u,int par){
	depth[u]=depth[par]+1;
	fa[u][0]=par;//u点上的2零次方节点为par
	for(int i=1;i<=29;i++)
	fa[u][i]=fa[fa[u][i-1]][i-1];
	//u的第2的i次方个节点等于u的i-1次方节点的第2的i-1个次方节点 
	for(int i=0;i=0;i--){
		if(dif>=bit[i]){//判断深度差是否大于2的i次方,以防止跳过
 			a=fa[a][i];//a往上调,深度减小 
			dif-=bit[i];
		}
	} 
	if(a==b)return a;
	for(int i=29;i>=0;i--){
		if(depth[a]>=bit[i]&&fa[a][i]!=fa[b][i])//深度足够并且a,b的第i个节点不同 
	{	a=fa[a][i];
		b=fa[b][i];
		} 
	} 
return fa[a][0];
}

​

​

 

 

经典模板题:    Nearest Common Ancestors(POJ1330)

题目大意:有t组测试数据,每组测试数据有n个点,前n-1行给出相连接的节点。(树可参照上图),第n行给出两个节点,求其最近公共祖先。

题解:根据最先公共祖先的套路,得先把所有的节点及其第2的i次方个节点放入一个数组,故此得先找出根节点。根节点的入度为0,在数据输入的时候需要统计一下每个点的入度。然后利用dfs将father[][]数组填充完整,进入LCA函数,最后输入就可。输入n-1个节点时,利用vector存图比较方便。

今天写这道题,因为主函数部分WA了好几次。首先是输入时,n-1组数据构建图,最后一对数是用来求最近公用祖先的;其次是对根节点的判断,要利用入度判断一下;最后,数组使用后要记得初始化,vector的初始化只能一个一个来,利用for循环,e[i].clear();.

AC代码:

#include
#include
#include
#include
#include
#include 
using namespace std;
const int N=1e4+10;
int f[N][30];
int depth[N];
long long bit[N];
int n;
int in[N];
int node;
int x,y;
vectore[N];
void init(){
	bit[0]=1;
	for(int i=1;i<=29;i++)
		bit[i]=bit[i-1]*2;
}
void dfs(int u,int par){
	depth[u]=depth[par]+1;
	f[u][0]=par;
	for(int i=1;i<=29;i++){
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for(int i=0;i=0;i--){
		if(dif>=bit[i])
		{
		a=f[a][i];
		dif-=bit[i];
		}
	}
	if(a==b) return a;
	for(int i=29;i>=0;i--){
		if(depth[a]>=bit[i]&&f[a][i]!=f[b][i]){
			a=f[a][i];
			b=f[b][i];
		}
	}
	return f[a][0];
}
int main(){
	int t,m;
	cin>>t;
	init();
	while(t--){
		memset(f,0,sizeof f);
		memset(depth,0,sizeof depth);	
		memset(in,0,sizeof in);	
		cin>>n;
		m=n-1;
		while(m--){
		cin>>x>>y;
		e[x].push_back(y);	
		e[y].push_back(x);
		in[y]++;
		}
		for(int i=1;i<=n;i++){
			if(in[i]==0)
			{
			node=i;
			break;}
		}		
		dfs(node,0);
		cin>>x>>y;
		printf("%d\n",lca(x,y));
		for(int i=1;i<=n;i++)
		e[i].clear();
	}
	return 0;
}

感觉今天学习的主题可以叫做找爸爸的爸爸hhhh~

博主还是蒟蒻,以上思路若有错误,请大佬们给指出!

你可能感兴趣的:(2019hpu暑期集训)