LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。
还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。
1.dfs序+RMQ求LCA
/* 用dfs序和时间戳 如果求m,n的LCA 记录m,n第一次出现的时间 在时间之内 找到一点有最小深度 此点就是lca 样例 5 4 1 2 1 3 2 4 2 5 4 5 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define M 1000 #define N 1000 using namespace std; int n,m;int num=0; int b[M],nt[M],p[N]; int a[N],height[N],ttime=0,first[N],minnum[N][N]; void insert(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); num++; b[num]=y; nt[num]=p[x]; p[x]=num; num++; b[num]=x; nt[num]=p[y]; p[y]=num; } } void dfs(int x,int h){ ++ttime; a[ttime]=x; height[ttime]=h; if(!first[x]) first[x]=ttime; for(int e=p[x];e;e=nt[e]) { int k=b[e]; if(!first[k]) dfs(k,h+1); ++ttime; a[ttime]=x; height[ttime]=h; } } void RMQ()//用RMQ求height中最小的结点 {//此时minnum中存的是最小的height对应的编号 for(int i=1;i<=ttime;i++) minnum[i][0]=i; int t=log2(ttime); for(int k=1;k<=t;k++){ for(int i=1; i<=ttime;i++) { if(height[minnum[i][k-1]]<=height[minnum[i+(1<<(k-1))][k-1]])//比较左区间最小的height对应的编号对应的height(就是最小的height)与右区间的...... minnum[i][k]=minnum[i][k-1]; else minnum[i][k]=minnum[i+(1<<(k-1))][k-1];//一定是1<<(k-1)不是k!!! } } } int lca(int x,int y){ if(x>y) swap(x,y); int k; int t=log2(ttime); if(height[minnum[x][t-1]]<height[minnum[y-t+1][t-1]]) k=minnum[x][t-1]; else k=minnum[y-t+1][t-1]; //printf("k=%d\n",k); return a[k]; } int main(){ insert(); dfs(1,0); RMQ(); int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(first[x],first[y])); return 0; }
2.倍增法求LCA(在线做法) 详细见注释
/* 样例: 10 9 1 2 1 3 2 4 2 5 3 6 5 7 5 8 7 9 7 10 9 6 (求9,6的lca) 思路 两步 1.将深度大的点翻到与深度小的点同一高度 2.两个点一起上翻 找lca 预处理fa二维数组 d一维数组 fa[x][y] 表示顶点x向上走2^y个顶点到达的点 d[x]数组是每个点的深度 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define M 1000 #define N 1000 using namespace std; int n,m;int num=0; int b[M],w[M],nt[M],p[N]; int fa[M][20],d[M]; void insert(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); num++; b[num]=y; nt[num]=p[x]; p[x]=num; num++; b[num]=x; nt[num]=p[y]; p[y]=num; } } void dfs(int x){ for(int i=1;i<=20;i++){//这里的20视题而定 这里表示最大深度为2^20
if(d[x]<(1<<i))break;
fa[x][i]=fa[fa[x][i-1]][i-1];
}//求fa数组的关键 和RMQ类似
for(int i=p[x];i>0;i=nt[i]){
int v=b[i];
if(v==fa[x][0]) continue;//无向边 不走已经走过的父节点
fa[v][0]=x;
d[v]=d[x]+1;//求深度=父节点深度+1
dfs(v);
}
}
int lca(int x,int y){
int h;
if(d[x]<d[y]) swap(x,y);
for(h=0;(1<<h)<=d[x];h++);
h--;
for(int i=h;i>=0;i--){
if(d[x]-(1<<i)>=d[y])
x=fa[x][i];
}//使a b深度相等
/*
或者可以写成
int t=d[x]-d[y];
for(int i=0;i<=20;i++)
{
if(t&(1<<i))
x=fa[x][i];
}
利用位运算 20的解释同上
*/
if(x==y) return x;//如果相等直接return
for(int i=h;i>=0;i--){//找lca 从大到小找(图中就是从上往下) lca是最下面的 公共父节点
if(fa[x][i]!=fa[y][i]){//关键:如果 不相等 fa[x][i]和fa[y][i]一定在lca的下面一个位置
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];//lca
}
int main(){
insert();
dfs(1);
int x,y;
scanf("%d%d",&x,&y);
printf("\nLCA=%d\n",lca(x,y));
return 0;
}
3.LCA求最短距离
/* 样例: 10 9 1 2 8 1 3 7 2 4 5 2 5 3 3 6 5 5 7 4 5 8 4 7 9 3 7 10 2 9 6 (求9,6的最短距离) 思路 求m到n最短距离 =m到lca的距离+n到lca的距离 =m到根的距离+n到根的距离-2*lca到根的距离 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define M 1000 #define N 1000 using namespace std; int n,m;int num=0; int b[M],w[M],nt[M],p[N]; int fa[M][20],d[M],val[N]; void insert(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); num++; b[num]=y; w[num]=z; nt[num]=p[x]; p[x]=num; num++; b[num]=x; w[num]=z; nt[num]=p[y]; p[y]=num; } } void dfs(int x){ for(int i=1;i<=20;i++){//这里的20视题而定 这里表示最大深度为2^20
if(d[x]<(1<<i))break;
fa[x][i]=fa[fa[x][i-1]][i-1];//求fa数组的关键 和RMQ类似
}
for(int i=p[x];i>0;i=nt[i]){
int v=b[i];
if(v==fa[x][0]) continue;//无向边 不走已经走过的父节点
fa[v][0]=x;
d[v]=d[x]+1;//求深度=父节点深度+1
val[v]=val[x]+w[i];//求与根结点距离 和深度类似
dfs(v);
}
}
int lca(int x,int y){//求LCA
int h;
if(d[x]<d[y]) swap(x,y);
for(h=0;(1<<h)<=d[x];h++);
h--;
for(int i=h;i>=0;i--){
if(d[x]-(1<<i)>=d[y])
x=fa[x][i];
}
if(x==y) return x;
for(int i=h;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int main(){
insert();
dfs(1);
int m,n;
scanf("%d%d",&m,&n);
printf("\nLCA=%d\n",lca(m,n));
printf("Distance=%d\n",val[m]+val[n]-2*val[lca(m,n)]);
return 0;
}