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;
}