您的位置:首页 > 其它

Tarjan算法求解最近公共祖先问题

2015-09-05 10:56 411 查看
起因是因为一道树形DP。。要用到求双连通分量和双连通缩点算法的算法。。于是学了一发。。

Tarjan算法可求解最近公共祖先问题(LCA),即给一颗有根树,给出若干个查询(u,v),每次查询给出u和v的最近公共祖先。

这个算法是基于dfs和并查集的。最开始从根开始搜索,对每个子树进行深搜。由于是后序遍历,那么可以发现这样一个性质,对于一个查询(u,v),假设其公共祖先为w,那么当访问到w时,必定已经访问完w所有的子树。根据这样一条性质,当我们每次访问完w的一棵子树时,就可以将该子树放入w所在的集合,这一操作可通过并查集完成。当访问完所有的子树后,所有子树的节点的最近公共祖先显然为w,这时可以根据查询求出答案并存储,不难看出这一算法是离线的。也有在线的算法,有兴趣的可以自行百度。

总的时间复杂度为O(n+q),dfs的时间复杂度为O(n),q次查询复杂度为O(q)。

Tarjan算法还可以用于求解双连通分量、割点、桥等各种问题,有兴趣的话可以学习一下。

例题:最近公共祖先问题,HDU2586 http://acm.hdu.edu.cn/showproblem.php?pid=2586
HDU2586代码:

// Header. Tarjan
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <set>
#include <map>
using namespace std;

// Macro
typedef long long LL;
#define TIME cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s." << endl;
#define IN freopen("/Users/apple/input.txt", "r", stdin);
#define OUT freopen("/Users/apple/out.txt", "w", stdout);
#define mem(a, n) memset(a, n, sizeof(a))
#define rep(i, n) for(int i = 0; i < (n); i ++)
#define repD(i, n) for(int i = (n); i; i --)
#define REP(i, t, n) for(int i = (t); i < (n); i ++)
#define REPD(i, t, n) for(int i = (n); i > (t); i --)
#define FOR(i, t, n) for(int i = (t); i <= (n); i ++)
#define FORD(i, t, n) for(int i = (n); i >= (t); i --)
#define ALL(v) v.begin(), v.end()
#define Min(a, b) a = min(a, b)
#define Max(a, b) a = max(a, b)
#define put(a) printf("%d\n", a)
#define ss(a) scanf("%s", a)
#define si(a) scanf("%d", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define VI vector<int>
#define pb push_back
#define x first
#define y second
const int inf = 0x3f3f3f3f, N = 4e4 + 5, MOD = 1e9 + 7;
// Macro end

int T, cas = 0;
int n, m, par
, vis
, dis
, ans
;
vector<pair<int, int> > son
;
vector<pair<int, int> > query
;
// Imp
int find(int x) {
if(x != par[x]) return par[x] = find(par[x]);
return x;
}

void Tarjan(int u) {
vis[u] = 1;
par[u] = u;
int len = son[u].size();
rep(i, len) {
int v = son[u][i].x;
if(!vis[v]) {
dis[v] = dis[u] + son[u][i].y;
Tarjan(v);
par[v] = u;
}
}
len = query[u].size();
rep(i, len) {
int v = query[u][i].x, idx = query[u][i].y;
if(vis[v]) ans[idx] = dis[u] + dis[v] - 2 * dis[find(v)];
}
}

#define LOCAL
int main(){
#ifdef LOCAL
IN // OUT
#endif

si(T);
while(T --) {
sii(n, m);
int u, v, d;
rep(i, N) son[i].clear(), query[i].clear();
mem(vis, 0);
rep(i, n - 1) {
siii(u, v, d);
son[u].pb(make_pair(v, d));
son[v].pb(make_pair(u, d));
}
rep(i, m) {
sii(u, v);
query[u].pb(make_pair(v, i));
query[v].pb(make_pair(u, i));
}
mem(vis, 0);
dis[1] = 0;
Tarjan(1);
rep(i, m) put(ans[i]);
}

return 0;
}


POJ 1470: http://poj.org/problem?id=1470
// Header.
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <bitset>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <set>
#include <map>
using namespace std;

// Macro
typedef long long LL;
#define TIME cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s." << endl;
#define IN freopen("/Users/apple/input.txt", "r", stdin);
#define OUT freopen("/Users/apple/out.txt", "w", stdout);
#define mem(a, n) memset(a, n, sizeof(a))
#define rep(i, n) for(int i = 0; i < (n); i ++)
#define repD(i, n) for(int i = (n); i; i --)
#define REP(i, t, n) for(int i = (t); i < (n); i ++)
#define REPD(i, t, n) for(int i = (n); i > (t); i --)
#define FOR(i, t, n) for(int i = (t); i <= (n); i ++)
#define FORD(i, t, n) for(int i = (n); i >= (t); i --)
#define ALL(v) v.begin(), v.end()
#define Min(a, b) a = min(a, b)
#define Max(a, b) a = max(a, b)
#define put(a) printf("%d\n", a)
#define ss(a) scanf("%s", a)
#define si(a) scanf("%d", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define VI vector<int>
#define pb push_back
const int inf = 0x3f3f3f3f, N = 1e3 + 5, MOD = 1e9 + 7;
// Macro end

int T, cas = 0;
int n, m, ne;
int head
, vis
, ans
, fa
, in
;
VI query
;
struct node {
int v, next;
}e[4 * N];
// Imp
void addEdge(int u, int v) {
e[ne].v = v, e[ne].next = head[u], head[u] = ne ++;
}

int find(int x) {
if(x != fa[x]) return fa[x] = find(fa[x]);
return x;
}

void Tarjan(int u) {
fa[u] = u;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(!vis[v]) {
Tarjan(v);
fa[v] = u;
}
}
vis[u] = 1;
int len = query[u].size();
rep(i, len) {
int v = query[u][i];
int k = find(v);
if(vis[v]) ans[k] ++;
}
}

void init() {
ne = 0;
mem(in, 0);
mem(ans, 0);
mem(vis, 0);
mem(head, -1);
rep(i, N) query[i].clear();
}

int main(){
#ifdef LOCAL
IN // OUT
#endif

while(si(n) != EOF) {
init();
int u, v;
rep(i, n) {
scanf("%d:(%d) ", &u, &m);
rep(j, m) si(v), in[v] ++, addEdge(u, v);
} si(m);
rep(i, m) {
while(getchar() != '(');
scanf("%d %d", &u, &v);
query[u].pb(v);
query[v].pb(u);
while(getchar() != ')');
}
FOR(i, 1, n) if(!in[i]) {
Tarjan(i);
break;
}
FOR(i, 1, n) if(ans[i]) printf("%d:%d\n", i, ans[i]);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: