POJ:1330-Nearest Common Ancestors(LCA在线、离线、优化算法)
传送门:http://poj.org/problem?id=1330
Nearest Common Ancestors
Time Limit: 1000MS Memory Limit: 10000K
Description
A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:
In the figure, each node is labeled with an integer from {1, 2,…,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is.
For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y.
Write a program that finds the nearest common ancestor of two distinct nodes in a tree.
Input
The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,…, N. Each of the next N -1 lines contains a pair of integers that represent an edge –the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.
Output
Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.
Sample Input
2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5
Sample Output
4
3
解题心得:
- 这是一个LCA的裸题,可以用用离线算法,也就是tarjan来做这个题,运用并查集,一棵树上的点分为三种,一种是已经找过的点,一种是正在找的点,还有一种是没有找过的点,如果其中一个点是没有找过的就不管,继续找下去,如果一个点是找过的,就直接用并查集回到他们共同的根节点(两个点必然在同一棵子树下),如果两个点都是正在找的点,那么其中一个点就是其最近祖先。
- 还有一种就是使用倍增法来做这个题,使用倍增法需要知道当前点的深度和父节点。两个点的最近公共祖先就是他们一起向上走第一次遇到的地方,运用这个性质就可以先将两个点的深度调到相同,然后一起向上走,第一次遇到的地方就是其最近公共祖先。
- 优化的思想也很简单,两个点一步一步的向上走是不是太慢了,可不可以多走几步,那怎么走呢?就可以想到使用RMQ的思想,按照二进制来走,在统一深度的时候可以将深度大的的那个点,走深度小的那个点的二进制中没有1的位置。然后一起向上面走二进制的步数,找打到第一个不是公共祖先的点然后返回他的父节点。思想比较简单,还是看实现过程吧。
tarjan写法:
#include<cstring>
#include<stdio.h>
#include<vector>
using namespace std;
const int maxn = 1e4+100;
int father[maxn],q1,q2,ans,n;
bool vis[maxn];
vector <int> ve[maxn];
void init()
{
memset(vis,0,sizeof(vis));
memset(father,0,sizeof(father));
for(int i=0;i<maxn;i++)
ve[i].clear();
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ve[a].push_back(b);
vis[b] = true;
}
scanf("%d%d",&q1,&q2);
}
int find(int x)
{
if(x == father[x])
return x;
return father[x] = find(father[x]);
}
void tarjan(int x)
{
father[x] = x;
for(int i=0;i<ve[x].size();i++)
{
int v = ve[x][i];
tarjan(v);
father[v] = x;
}
if(x == q1 || x == q2)
{
if(x != q1)
swap(q1,q2);
if(father[q2])//如果其中一个点没被找到那么就继续找下去
ans = find(father[q2]);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
tarjan(i);
break;
}
}
printf("%d\n",ans);
}
return 0;
}
倍增写法(无优化)
#include<stdio.h>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1e4+100;
vector <int> ve[maxn];
int n,father[maxn],dep[maxn];
bool vis[maxn];
void init()
{
for(int i=0;i<=n;i++)
ve[i].clear();
memset(father,0,sizeof(father));
memset(dep,0,sizeof(dep));
memset(vis,0,sizeof(vis));
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
vis[b] = true;
ve[a].push_back(b);
}
}
int dfs(int u,int f,int d)
{
father[u] = f;//记录父节点
dep[u] = d;//记录深度
for(int i=0;i<ve[u].size();i++)
{
int v = ve[u][i];
if(v == f)//主要是处理单向边
continue;
dfs(v,u,d+1);
}
}
void LCA(int q,int p)
{
if(dep[p] > dep[q])
swap(q,p);
while(dep[q] > dep[p])//调节到同一深度
q = father[q];
while(p != q)//一起向上走
{
p = father[p];
q = father[q];
}
printf("%d\n",q);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
if(!vis[i])
{
dfs(i,-1,0);
break;
}
int q,p;
scanf("%d%d",&q,&p);
LCA(q,p);
}
}
LCA进过优化后的代码:
#include<stdio.h>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e4+100;
const int LOG = 33;
int p[maxn][33],dep[maxn],n;
bool vis[maxn];
vector <int> ve[maxn];
void init()
{
for(int i=0;i<=n;i++)
ve[i].clear();
memset(dep,0,sizeof(dep));
memset(p,0,sizeof(p));
memset(vis,0,sizeof(vis));
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ve[a].push_back(b);
vis[b] = true;
}
}
void dfs(int u,int f,int d)
{
p[u][0] = f;//u点向前移动2的0次方位为它的父节点
dep[u] = d;
for(int i=0;i<ve[u].size();i++)
{
int v = ve[u][i];
if(v == f)
continue;
dfs(v,u,d+1);
}
}
int LCA(int x,int y)
{
for(int i=0;i+1<LOG;i++)
for(int j=1;j<=n;j++)
if(p[j][i] < 0) p[j][i+1] = -1;//树中的节点向上移动超出了根节点都为-1
else p[j][i+1] = p[p[j][i]][i];//否则RMQ思想
if(dep[y] > dep[x])
swap(y,x);
for(int i=0;i<LOG;i++)
if(dep[x] - dep[y] >> i & 1)//向上移动到同一深度的时候,将更深的节点二进制表示中多出部分的1移走就行了
x = p[x][i];
if(x == y)//同一深度的时候已经合一了
return x;
for(int i=LOG-1;i>=0;i--)//找到向上移动中最大祖先的下面第一个节点
{
if(p[x][i] != p[y][i])
{
x = p[x][i];
y = p[y][i];
}
}
return p[x][0];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
if(!vis[i])
{
dfs(i,-1,0);
break;
}
int p,q;
scanf("%d%d",&p,&q);
printf("%d\n",LCA(p,q));
}
}
POJ:1330-Nearest Common Ancestors(LCA在线、离线、优化算法)的更多相关文章
- poj 1330 Nearest Common Ancestors lca 在线rmq
Nearest Common Ancestors Description A rooted tree is a well-known data structure in computer scienc ...
- POJ.1330 Nearest Common Ancestors (LCA 倍增)
POJ.1330 Nearest Common Ancestors (LCA 倍增) 题意分析 给出一棵树,树上有n个点(n-1)条边,n-1个父子的边的关系a-b.接下来给出xy,求出xy的lca节 ...
- POJ 1330 Nearest Common Ancestors LCA题解
Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 19728 Accept ...
- POJ 1330 Nearest Common Ancestors (LCA,倍增算法,在线算法)
/* *********************************************** Author :kuangbin Created Time :2013-9-5 9:45:17 F ...
- poj 1330 Nearest Common Ancestors LCA
题目链接:http://poj.org/problem?id=1330 A rooted tree is a well-known data structure in computer science ...
- POJ 1330 Nearest Common Ancestors(LCA模板)
给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果 ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- POJ 1330 Nearest Common Ancestors 倍增算法的LCA
POJ 1330 Nearest Common Ancestors 题意:最近公共祖先的裸题 思路:LCA和ST我们已经很熟悉了,但是这里的f[i][j]却有相似却又不同的含义.f[i][j]表示i节 ...
- POJ - 1330 Nearest Common Ancestors(基础LCA)
POJ - 1330 Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000KB 64bit IO Format: %l ...
- POJ 1330 Nearest Common Ancestors(lca)
POJ 1330 Nearest Common Ancestors A rooted tree is a well-known data structure in computer science a ...
随机推荐
- python 6 循环
循环 要计算1+2+3,我们可以直接写表达式: >>> 1 + 2 + 3 6 要计算1+2+3+...+10,勉强也能写出来. 但是,要计算1+2+3+...+10000,直接写表 ...
- MapReduce错误之Error: java.lang.RuntimeException: java.lang.NoSuchMethodException的解决方法
今天跑MapReduce项目的时候遇到了这个问题,日志如下所示: // :: DEBUG ipc.ProtobufRpcEngine: Call: getDiagnostics took 19ms E ...
- Python网络编程中的服务器架构(负载均衡、单线程、多线程和同步、异步等)
这篇文章主要介绍服务器架构. 网络服务需要面对两个挑战. 第一个问题是核心挑战,要编写出能够正确处理请求并构造合适响应的代码. 第二个挑战是如何将网络代码部署到随系统自动启动的Windows服务或者是 ...
- Java命令行实用工具jps和jstat 专题
在Linux或其他UNIX和类UNIX环境下,ps命令想必大家都不陌生,我相信也有不少同学写过 ps aux | grep java | grep -v grep | awk '{print $2}' ...
- FirstAFNetWorking
// ViewController.h // FirstAFNetWorking // // Created by 张国锋 on 15/7/20. // Copyright (c) 2015年 张国锋 ...
- 一步步实现自己的ORM(二)
在第一篇<一步步实现自己的ORM(一)>里,我们用反射获取类名.属性和值,我们用这些信息开发了简单的INSERT方法,在上一篇文章里我们提到主键为什么没有设置成自增长类型,单单从属性里我们 ...
- Linux 系统挂载阿里云数据盘
适用系统:Linux(Redhat , CentOS,Debian,Ubuntu) * Linux的云服务器数据盘未做分区和格式化,可以根据以下步骤进行分区以及格式化操作. 下面的操作将会把数据盘划 ...
- 洛谷 P1145 约瑟夫
题目描述 n个人站成一圈,从某个人开始数数,每次数到m的人就被杀掉,然后下一个人重新开始数,直到最后只剩一个人.现在有一圈人,k个好人站在一起,k个坏人站在一起.从第一个好人开始数数.你要确定一个最小 ...
- UVA 536 TreeRocvery 树重建 (递归)
根据先序历遍和中序历遍输出后序历遍,并不需要真的建树,直接递归解决 #include<cstdio> #include<cstring> ; char preOrder[N]; ...
- [opencv] applyColorMap
applyColorMap 功能 转化为热力图,因为热力图我们看的变化更加细微,在很多地方都用到了热力图. 最近在看CAM,所以记一记这个函数. 感觉还是很有用的. 代码 >>> i ...