树的最近公共祖先——LCA poj1330 hdu2586

树的最近公共祖先问题,是指给定一棵有向树(无向的可以转化为有向的),对于询问q(u,y),返回u和v的最近公共祖先。

解决问题算法可以分为在线算法和离线算法。

离线算法思想:

离线算法需要预先将所有的询问存起来,等到dfs的时候一下逐一输出结果。

dfs对树进行遍历,当某个节点u的一个子树全部遍历完以后,就更新这棵子树的节点的父节点为u(转化为并查集:将该子树集合加入到u所在的集合中,并更新子树集合的父亲为u)如果当前节点x是被询问到的节点,并且一直相关的(询问了x,y的最近公共祖先)另一个节y点已经搜索过,则x与y的最近公共祖先为y此时的父亲。

在线算法的思想:

在线算法的思想比较简单,先对整棵树进行dfs,并记下每个节点的深度以及父节点,当询问某对节点时,比较两个节点的深度,深度大的节点开始沿着父节点往回走,当两个节点的深度相同时,判断其是否为同一个节点,若是,则输出,否则,两节点同时沿着各自的父亲节点往回走,直到两节点相同,输出。

个比较喜欢用在线算法,而且优化后速度也很快。

刚开始学习这个算法的时候也在网上找了许多资料,但是还是觉得这片文章写得很好;

http://blog.csdn.net/v_july_v/article/details/18312089

看完算法后,我自己也找了几道简单的题目做了做;

poj1330 离线算法 

#include<cstdio>
#include<cstring>
#include<vector>
#define MAX 10010

using namespace std;

vector<int>G[MAX];

int fa[MAX],lca[MAX],c[MAX],vis[MAX],u,v,ans;

int find(int x){                    //并查集
    return x == fa[x] ? x : (fa[x] = find(fa[x]));
}

void merge(int x, int y){
    x = find(x);
    y = find(y);
    if (x != y)
        fa[x] = y;
}

void LCA(int root){               //最近公共祖先
    for (int i = 0; i<G[root].size(); i++){
        LCA(G[root][i]);
        merge(G[root][i],fa[root]);   //如何合并?
    }
    vis[root] = 1;
    if (root == u && vis[v]){
        printf("%d\n",find(v));
        return;
    }
    else if (root == v && vis[u]){
        printf("%d\n",find(u));
        return;
    }
}

int main(){
    int T,n,root;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);                          //初始化并输入数据
        for (int i = 0; i<MAX; i++) G[i].clear();
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
        for (int i = 1; i<=n; i++){
            fa[i] = i;
            lca[i] = i;
        }

        int a,b;
        for (int i = 0; i<n-1; i++){
            scanf("%d%d",&a,&b);
            c[b]++;
            G[a].push_back(b);
        }

       for (int i = 1; i<=n; i++) if (c[i] == 0){          //找到根节点
           root = i;
           break;
       }

       scanf("%d%d",&u,&v);
       LCA(root);

    }
    return 0;
}


hdu2586在线算法;

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAX 40010

using namespace std;

struct Edge{
	int from,to,dist;
};

vector<int>G[MAX];
vector<Edge>edges;

int D[MAX], d[MAX], fa[MAX], in[MAX];
bool vis[MAX];

void init(){
	edges.clear();
	for (int i=0; i<MAX; i++) G[i].clear();
	memset(vis,false,sizeof(vis));
	memset(d,0,sizeof(d));
	memset(in,0,sizeof(in));
}

void AddEdge(int from, int to, int dist){
	edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);
}

void dfs(int pa, int u, int depth){
	D[u] = depth;
	vis[u] = true;
	fa[u] = pa;
	for (int i=0; i<G[u].size(); i++){
		Edge& e = edges[G[u][i]];
		if (vis[e.to]) continue;
		d[e.to] = d[u] + e.dist;
		dfs(u,e.to,depth+1);
	}
}

int LCA(int u, int v){
	int x = D[u];
	int y = D[v];
	if (x < y) {
		swap(u,v);
		swap(x,y);
	}
	while (x > y){
		u = fa[u];
		x--;
	}
	if (u == v) return v;
	while (u != v){
		u = fa[u];
		v = fa[v];
	}
	return v;
}

int main(){
	int T,n,m;
	scanf("%d",&T);
	while (T--){
		
		init();
		
		scanf("%d%d",&n,&m);
		int x,y,z;
		for (int i = 1; i<n; i++){
			scanf("%d%d%d",&x,&y,&z);
			in[y]++;
			AddEdge(x,y,z);
		}
		
		int root;
		for (int i = 1; i<=n; i++) if (!in[i]){
			root = i;
			break;
		}
		
		dfs(-1,root,0);
	//	for (int i = 1; i<=n; i++) printf("%d ",D[i]); printf("\n");
		
		int u,v;
		for (int i = 0; i<m; i++){
			scanf("%d%d",&u,&v);
			int w = LCA(u,v);
			printf("%d\n",d[u]+d[v]-2*d[w]);
		}
	}
	return 0;
}


hdu2586在线算法 + ST优化;

#include<cstdio>
#include<cstring>
#include<vector> 
#include<algorithm>
#define MAX 80010 

using namespace std;

struct Edge{
	int form, to, dist;
};

int E[MAX],D[MAX],R[MAX],in[MAX],dp[MAX][30],d[MAX],cnt;  //cnt = -1
bool vis[MAX];

vector<int>G[MAX];
vector<Edge>edges;

void AddEdge(int from, int to, int dist){
    edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);	
}

void dfs(int fa, int u, int depth){
	E[++cnt] = u;
	D[u] = depth;
	R[u] = cnt;
	vis[u] = true;
	for (int i = 0; i<G[u].size(); i++){
		Edge& e = edges[G[u][i]];
		if (vis[e.to]) continue;	
		d[e.to] = d[u] + e.dist;
		dfs(u,e.to,depth+1);
		E[++cnt] = u;
	}
}

void RMQ(){
	for (int i = 0; i < cnt; i++) dp[i][0] = E[i];
	for (int j = 1; (1<<j) < cnt; j++){
		for (int i = 0; i<cnt-(1<<j)+1; i++){
			if (D[dp[i][j-1]] <= D[dp[i+(1<<(j-1))][j-1]])
				dp[i][j] = dp[i][j-1];
			else
				dp[i][j] = dp[i+(1<<(j-1))][j-1];
		}
	}
}

int LCA(int u, int v){
	u = R[u];
	v = R[v];
	if (u > v) swap(u,v);
	int k = 0;
	while ((1<<k) <= (v-u+1)) k++;
	k--;
	if (D[dp[u][k]] < D[dp[v-(1<<k)+1][k]]) return dp[u][k];
	return dp[v-(1<<k)+1][k];
}

int main(){
	int T,n,m;
	scanf("%d",&T);
	while (T--){
		
		edges.clear();
		for (int i = 0; i<MAX; i++) G[i].clear();
		memset(vis,false,sizeof(vis));
		memset(in,0,sizeof(in));
		cnt = -1;
		
		scanf("%d%d",&n,&m);
		int x,y,z;
		for (int i = 1; i<n; i++){
			scanf("%d%d%d",&x,&y,&z);
			in[y]++;
			AddEdge(x,y,z);
		}
		
		int root;
		for (int i = 1; i<=n; i++) if (!in[i]){
			root = i;
			break;
		}
		
		dfs(-1,root,0);
		
		//for (int i = 1; i<=n; i++) printf("%d ",d[i]); printf("\n");
		
		cnt += 1;       
		RMQ();
		
		int u, v;
		for (int i = 0; i<m; i++){
			scanf("%d%d",&u,&v);
			int w = LCA(u,v);
			printf("%d\n",d[u] + d[v] - 2*d[w]);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值