poj1330Nearest Common Ancestors LCA问题 dfs+rmq

题目意思很简单就是求两个节点的LCA,这个问题可以转化为rmq问题,求区间的最小值。

就是首先利用dfs遍历图的所有顶点并且每条边会遍历两次,这样遍历的顶点总共2*n-1个,依次将遍历的边存在数组e[i]中,并且记录每个顶点的深度,

存入数组level[i]中,我们再开一个数组存每一个顶点首次出现的下标,记录在idx中,这样任意给出两个顶点,我们求出他们对应的下标x,y,然后根据level数组在区间[x,y]中找出深度最小的下标r,这个下标对应的e就是我们要求的节点。这个题目就是将LCA转化为在某段区间中找高度最小的那个顶点。

代码如下:

#include <iostream>
#include
<stdio.h>
#include
<cmath>
using namespace std;
const int N=10001;
bool visit[N];
struct node{
int to,next;
};
node edge[N];
int idx[N],e[2*N],adj[N],num=0,n,level[2*N];
int g_index,m[2*N][15];
void dfs(int u,int depth)
{
int i,v;
for(i=adj[u];i!=-1;i=edge[i].next)
{
v
=edge[i].to;
e[g_index]
=v;
level[g_index]
=depth+1;
// cout<<e[g_index]<<" "<<level[g_index]<<endl;
if(-1 == idx[v])
idx[v]
=g_index;
g_index
++;
dfs(v,depth
+1);
e[g_index]
=u;
level[g_index]
=depth;
// cout<<e[g_index]<<" "<<level[g_index]<<endl;
g_index++;
}

}
void initrmq()
{
int i,j;
for(i=0;i<=2*n-1;i++) //写成了i<=n,查了半天
m[i][0]=i;
for(j=1;(1<<j)<=2*n-1;j++)
{
for(i=1;i+(1<<j)-1<=2*n-1;i++)
{
if(level[m[i][j-1]] < level[m[i+(1<<(j-1))][j-1]])
m[i][j]
=m[i][j-1];
else
m[i][j]
=m[i+(1<<(j-1))][j-1];
}
}
}
int query(int u,int v)
{
int x=idx[u];
int y=idx[v];
if(x>y)
{
int tmp=x;
x
=y;
y
=tmp;
}
int k=(int)((log(1.0*(y-x+1))/log(2.0)));
int rs=m[x][k];
if(level[rs]>level[m[y-(1<<k)+1][k]])//一开始写成>,这种错误我都能犯
rs=m[y-(1<<k)+1][k];
return e[rs];
}
int main()
{
int t,i,u,v;
scanf(
"%d",&t);
while(t--)
{
memset(adj,
-1,sizeof(adj));
memset(level,
-1,sizeof(level));
memset(visit,
false,sizeof(visit));
memset(idx,
-1,sizeof(idx));
memset(m,
0,sizeof(m));
num
=0;
g_index
=1;
scanf(
"%d",&n);
for(i=1;i<n;i++)
{
scanf(
"%d%d",&u,&v);
visit[v]
=true;
edge[num].to
=v;
edge[num].next
=adj[u];
adj[u]
=num++;
}
for(i=1;i<=n;i++)
{
if(!visit[i])
{
level[g_index]
=1;
idx[i]
=g_index;
e[g_index]
=i;
g_index
++;
dfs(i,
1);
}
}
initrmq();
scanf(
"%d%d",&u,&v);
printf(
"%d\n",query(u,v));
}
return 0;
}
原文地址:https://www.cnblogs.com/buptLizer/p/2179233.html