码迷,mamicode.com
首页 > 其他好文 > 详细

poj1330 lca 最近公共祖先问题学习笔记

时间:2015-08-14 21:03:48      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:

首先推荐两个博客网址:

http://dongxicheng.org/structure/lca-rmq/

http://scturtle.is-programmer.com/posts/30055.html

[转]tarjan算法的步骤是(当dfs到节点u时):
1 在并查集中建立仅有u的集合,设置该集合的祖先为u
1 对u的每个孩子v:
   1.1 tarjan之
   1.2 合并v到父节点u的集合,确保集合的祖先是u
2 设置u为已遍历
3 处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=v所在的集合的祖先
 
举例说明(非证明):

技术分享
假设遍历完10的孩子,要处理关于10的请求了
取根节点到当前正在遍历的节点的路径为关键路径,即1-3-8-10
集合的祖先便是关键路径上距离集合最近的点
比如此时:
    1,2,5,6为一个集合,祖先为1,集合中点和10的LCA为1
    3,7为一个集合,祖先为3,集合中点和10的LCA为3
    8,9,11为一个集合,祖先为8,集合中点和10的LCA为8
    10,12为一个集合,祖先为10,集合中点和10的LCA为10
你看,集合的祖先便是LCA吧,所以第3步是正确的
道理很简单,LCA(u,v)便是根至u的路径上到节点v最近的点

为什么要用祖先而且每次合并集合后都要确保集合的祖先正确呢?
因为集合是用并查集实现的,为了提高速度,当然要平衡加路径压缩了,所以合并后谁是根就不确定了,所以要始终保持集合的根的祖先是正确的
关于查询和遍历孩子的顺序:
wikipedia上就是上文中的顺序,很多人的代码也是这个顺序
但是网上的很多讲解却是查询在前,遍历孩子在后,对比上文,会不会漏掉u和u的子孙之间的查询呢?
不会的
如果在刚dfs到u的时候就设置u为visited的话,本该回溯到u时解决的那些查询,在遍历孩子时就会解决掉了
这个顺序问题就是导致我头大看了很久这个算法的原因,也是絮絮叨叨写了本文的原因,希望没有理解错= =

对于这道题

题意:求最近公共祖先lca

下面是学来的tarjan代码

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <cmath>
  7 #include <ctime>
  8 
  9 using namespace std;
 10 
 11 class Edge
 12 {
 13     public:
 14     int    to;
 15     int    next;
 16 }e[11000];
 17 
 18 int    n,T,cnt;
 19 int    f[11000],depth[11000],anc[11000],p[11000];
 20 bool    visited[11000],In[11000];
 21 
 22 inline    void    Add_edge(const int & x,const int & y)
 23 {
 24     e[++cnt].to=y;
 25     e[cnt].next=p[x];
 26     p[x]=cnt;
 27     return ;
 28 }
 29 
 30 inline    int    get_anc(const int &x)
 31 {
 32     return f[x]==x ? x:f[x]=get_anc(f[x]);
 33 }
 34 
 35 inline    void    Union(const int & x,const int & y)
 36 {
 37     int    S,T;
 38     S=get_anc(x);
 39     T=get_anc(y);
 40     if(S==T)return ;
 41     if(depth[S]<=depth[T])
 42         f[S]=T,depth[S]+=depth[T];
 43     else
 44         f[T]=S,depth[T]+=depth[S];
 45     return ;
 46 }
 47 
 48 void    Dfs(const int &S,const int d)
 49 {
 50     int    i;
 51     depth[S]=d;
 52     for(i=p[S];i;i=e[i].next)
 53     {
 54         Dfs(e[i].to,d+1);
 55     }
 56 
 57     return ;
 58 }
 59 
 60 int    Lca_tarjan(const int & s,const int & t,const int & u)
 61 {
 62     int    i,temp;
 63     
 64     anc[u]=u;
 65     for(i=p[u];i;i=e[i].next)
 66     {
 67         temp=Lca_tarjan(s,t,e[i].to);
 68         if(temp)return temp;
 69         Union(u,e[i].to);
 70         anc[get_anc(u)]=u;
 71     }
 72 
 73     visited[u]=true;
 74     if(s==u&&visited[t])
 75         return anc[get_anc(t)];
 76     if(t==u&&visited[s])
 77         return anc[get_anc(s)];
 78 
 79     return 0;
 80 }
 81 
 82 inline    void    Init()
 83 {
 84     cnt=0;
 85     memset(depth,0,sizeof(depth));
 86     memset(visited,0,sizeof(visited));
 87     memset(anc,0,sizeof(anc));
 88     memset(p,0,sizeof(p));
 89     memset(e,0,sizeof(e));
 90     memset(In,0,sizeof(In));
 91 
 92     for(int i=1;i<=n;++i)
 93     {
 94         f[i]=i;
 95     }
 96 
 97     return ;
 98 }
 99 
100 int main()
101 {
102     //freopen("1330.in","r",stdin);
103     
104     int    i,x,y,s,t,S;
105     
106     scanf("%d",&T);
107     while(T--)
108     {
109         scanf("%d",&n);
110         Init();
111         for(i=1;i<n;++i)
112         {
113             scanf("%d%d",&x,&y);
114             Add_edge(x,y);
115             In[y]=true;
116         }
117         
118         for(S=1;S<=n;++S)
119             if(!In[S])break;
120 
121         scanf("%d%d",&s,&t);
122         Dfs(S,1);
123         printf("%d\n",Lca_tarjan(s,t,S));
124     }
125     
126     return 0;
127 }

 

poj1330 lca 最近公共祖先问题学习笔记

标签:

原文地址:http://www.cnblogs.com/Gster/p/4730921.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!