最近公共祖先問題(LCA)-Tarjan演算法
阿新 • • 發佈:2020-08-06
Tarjan演算法的實現有很多方法,這裡我們記錄的是並查集維護下的Tarjan離線演算法
【離線演算法】指基於在執行演算法前輸入資料已知的基本假設,也就是說,對於一個離線演算法,在開始時就需要知道問題的所有輸入資料,而且在解決一個問題後就要立即輸出結果。
例題-POJ.1470-Closest Common Ancestors
首先獻上一個TLE了的程式碼,我還不知道到底怎麼會TLE了的。
希望有個大神能幫忙看一下。如果你是新手,請跳過這段程式碼繼續向下看。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; class edge{ public: int v,nxt; edge(){}; edge(int x,int y):v(x),nxt(y){}; }; class query{ public: int x,y; bool ok; query(int a,int b):x(a),y(b),ok(false){}; bool operator< (const query& rhs)const{return x<rhs.x;} bool check(int u){if(ok)return false;return (x==u||y==u);} int mate(int u){return (x+y-u);} }; const int maxn = 1000; int n; int m; vector<query> psd; int fa[maxn]; int father[maxn]; edge mapper[maxn]; int head[maxn]; int vis[maxn]; int ans[maxn]; int cnt; void init() { for(int i=0;i<=n;i++) fa[i]=father[i]=i; int p = n+3; memset(head,-1,sizeof(head)); memset(ans,0,sizeof(ans)); memset(vis,0,sizeof(vis)); psd.clear(); cnt=0; } void add(int u,int v) { mapper[cnt] = edge(u,head[v]); head[v] = cnt++; } int readin() { //cout<<"nump is "<<nump<<endl; if(scanf("%d",&n)<=0)return 0; init(); for(int i=0;i<n;i++) { int v,num; scanf("%d:(%d)",&v,&num); for(int k=0;k<num;k++) { int p; scanf("%d",&p); add(p,v); father[p]=v; } } scanf("%d",&m); int sp = m; while(sp--) while(1) { char ch;int a,b; ch = getchar(); if(ch=='(') { scanf(" (%d %d)",&a,&b); //scanf("%d%*c%d",&a,&b); psd.push_back(query(a,b)); getchar();break; } } int root=1; while(father[root]!=root)root=father[root]; return root; } int find(int u){return (fa[u]==u)? u : fa[u]=find(fa[u]);} int LCA(int u) { if(vis[u])return 0;; for(int i = head[u];~i;i=mapper[i].nxt) { int p = mapper[i].v; if(!vis[p]){LCA(p);fa[p]=u;} } for(int k=0;k<m;k++) { if(psd[k].check(u) && vis[psd[k].mate(u)]) { ans[find(psd[k].mate(u))]++; psd[k].ok=true; } } vis[u]=1; return 0; } int solve() { int root=readin(); if(!root)return 0; LCA(root); for(int i=1;i<=n;i++) { if(ans[i])printf("%d:%d\n",i,ans[i]); } return 1; } int main() { //freopen("in.txt","r",stdin); while(solve()); return 0; }
下面是一個AC的程式碼(這個程式碼寫得還是很不錯的)
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 1004; //採用maxn*maxn的大小 struct node{//放置樹 int u,v,next; } g[maxn*maxn]; struct nod{//放置問題 int u,v,next; } G[maxn*maxn]; int n,m; int head[maxn],hd[maxn]; int tot; int res[maxn]; int vis[maxn],pre[maxn],fa[maxn]; void init() { tot=0; memset(vis,0,sizeof(vis)); memset(fa,-1,sizeof(fa)); memset(res,0,sizeof(res));//清空答案陣列 memset(head,-1,sizeof(head)); memset(hd,-1,sizeof(hd)); for(int i=1;i<=n;i++) pre[i]=i; } void addg(int u,int v) { g[tot].v = v; g[tot].next = head[u]; head[u] = tot++; } void addG(int u,int v) { G[tot].v = v; G[tot].next = hd[u]; hd[u] = tot++; } int Find(int x) { return (x==pre[x])? x : pre[x]=Find(pre[x]); } //核心函式 void lca(int u,int fa) {//這裡加入上一級的父親節點fa其實是為了適配無向路 for(int i=head[u];~i;i=g[i].next) { int v=g[i].v; if(v==fa)continue;//用來避免無向路帶來的dfs死迴圈 if(!vis[v]) { lca(v,u); pre[v]=u; } } vis[u]=1;//一定要在u點的子樹搜尋完成後才能標記 for(int i=hd[u];~i;i=G[i].next) {//這裡將詢問也按照鄰接表的形式進行儲存 int v=G[i].v; if(vis[v]) res[Find(v)]++; } //vis[u]=1 也可以寫在這裡 } int main() { while(~scanf("%d",&n)) { init(); int a,b,c; for(int i=0;i<n;i++) { scanf("%d:(%d)",&a,&b); for(int j=0;j<b;j++) { scanf("%d",&c); addg(a,c); addg(c,a); fa[c]=a; } } int root=1; while(fa[root]!=-1)root=fa[root]; scanf("%d",&m); tot=0; for(int i=0;i<m;i++) { scanf(" (%d %d)",&a,&b); //這句輸入中有一個空格,沒有的話就會MLE addG(a,b); addG(b,a); } lca(root,root); for(int i=1;i<=n;i++) { if(res[i]) printf("%d:%d\n",i,res[i]); } } return 0; }
然後是我參考的幾篇相對好一些的文章
LCA核心函式虛擬碼模板
vis[s]:s是否被訪問的標記-初值是False Father[s]:s的父親節點-初值是s CommonAncestor[a,b]:指a,b兩節點的最近公共祖先 Querys:所有詢問的集合 /*Find是並查集的維護程式碼*/ Find(x){return (x==father[x])?x:father[x]=Find(father[x]);} LCA(vertex,father): BEGIN 1、FOR u OF ALL SONs NODE OF vertex: IF u == father : conintue; IF vis[u] == False : LCA(u,vertex); Father[u]=vertex; 2、vis[u]=True; 3、FOR query(u,t) IN Querys: IF vis[t]==True: CommonAncestor[a,b] = Find(t); END
OK