Closest Common Ancestors (Lca,tarjan)
Posted zjydeoneday
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Closest Common Ancestors (Lca,tarjan)相关的知识,希望对你有一定的参考价值。
午时刷题,难甚,遂小憩于桌上,惊醒,于梦中有所得,虽大声曰:吾已得tarjan之奥秘!
关于tarjan算法,其实就是一个递归加并查集的应用。
大致代码:
#include<bits/stdc++.h> using namespace std; int find(int x) .... void join(int x,int y) .... void dfs(int x) int len=v[x].size(); for(int i=0; i<len; i++) //遍历x的子节点 dfs(v[x][i]); //继续往下推 join(v[x][i],x); //将x的所有子节点的祖先都设为x vis[x] = true; //证明x走过了 for(int i=1; i<=n; i++) //对每个x循环1~n if(vis[i]&&g[x][i]) //如果i已经走过并且要求(x,i) ans=find(i); //lca就是ans
由以上代码可以看出,tarjan实际上就是并查集与dfs的结合,其最核心的部分就是dfs那部分
只要理解了dfs()的内容,就能理解tarjan
而对于dfs函数,我们首先就会想到它的特性:不撞南墙不回头。
假如有一颗树,对其dfs,那么首先它会沿着一个分支一直到尽头(如图):
而当走到4这个点时,函数开始执行下列语句:
join(v[x][i],x); //将x的所有子节点的祖先都设为x
//而此时pre[4]=3;pre[3]=3;pre[2]=2;pre[1]=1;
再然后是:
vis[x] = true; //证明x走过了 for(int i=1; i<=n; i++) //对每个x循环1~n if(vis[i]&&g[x][i]) //如果i已经走过并且要求(x,i) ans=find(i); //lca就是ans
如果存在要求lca[x][i],先看i点是否走过,如果走过,那就只有一种可能(真相只有一个!真実はいつも一つ):
i,k必在两条不同分支上,并且交于某个点x,如果i已经走过了,那么,i所在的这条分支上所有的点都有明确的父子关系,即find(i)==x!
代码参上:
#pragma GCC optimize(2) #include<stdio.h> #include<string.h> #include<vector> #define M 1007 using namespace std; int g[M][M],in[M],pre[M],cnt[M]; bool vis[M]; vector<int>v[M]; int n,m; void init() memset(g,0,sizeof(g)); memset(in,0,sizeof(in)); memset(cnt,0,sizeof(cnt)); memset(vis,false,sizeof(vis)); for(int i=1; i<=n; i++) v[i].clear(); pre[i]=i; int fond(int x) return x==pre[x]?x:pre[x]=fond(pre[x]); void join(int x,int y) int xx=fond(x); int yy=fond(y); pre[xx]=yy; void dfs(int x) int len=v[x].size(); for(int i=0; i<len; i++) //遍历x的子节点 dfs(v[x][i]); //继续往下推 join(v[x][i],x); //将x的所有子节点的祖先都设为x vis[x] = true; //证明x走过了 for(int i=1; i<=n; i++) //对每个x循环1~n if(vis[i]&&g[x][i]) //如果i已经走过并且要求(x,i) cnt[fond(i)]+=g[x][i]; // int main() while(scanf("%d",&n)!=EOF) init(); int a,b,c,root; for(int i=1; i<=n; i++) scanf("%d:(%d)",&a,&b); while(b--) scanf(" %d",&c); v[a].push_back(c); in[c]++; scanf("%d",&m); getchar(); while(m--) scanf("(%d,%d)",&a,&b); getchar(); g[a][b]++; g[b][a]++; for(int i=1; i<=n; i++) if(!in[i]) root=i; break; dfs(root); for(int i=1; i<=n; i++) if(cnt[i]) printf("%d\\n",i); return 0;
以上;
以上是关于Closest Common Ancestors (Lca,tarjan)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 1470 -- Closest Common Ancestors
POJ 1470 Closest Common Ancestors LCA
LCA/tarjanPOJ1470-Closest Common Ancestors
Closest Common Ancestors (Lca,tarjan)