题目大意:已知一个树的前序和中序序列,求两个结点的最近公共祖先
本着试一试的做法,直接建树,然后套倍增求LCA的算法,还好数据不是很大,没卡爆数组。
法1:
#include <iostream>
#include <unordered_set>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N],dis[N*100],fa[N*100][16];
struct node
{
int v;
node*left,*right;
node(int v) : v(v),left(NULL),right(NULL){}
}*tr;
unordered_set<int>se;
node *build(int preL,int preR,int inerL,int inerR)//前中建树
{
if(preL > preR) return NULL;
int k = inerL;
node* t = new node(pre[preL]);
se.insert(pre[preL]);
while(pre[preL] != iner[k]) k ++;//前序第一个结点就是根,在中序里面找根
t -> left = build(preL + 1,preL + k - inerL,inerL,k - 1);
t -> right = build(preL + k - inerL + 1,preR,k + 1,inerR);
return t;
}
void dfs(node *t,int d)
{
if(t->left != NULL)
{
int a = t ->left ->v;
if(dis[a] > d + 1)
{
dis[a] = d + 1;fa[a][0] = t->v;
for(int k = 1; k <= 15; k ++) fa[a][k] = fa[fa[a][k - 1]][k - 1];
dfs(t->left,d + 1);
}
}
if(t->right != NULL)
{
int a = t ->right ->v;
if(dis[a] > d + 1)
{
dis[a] = d + 1;fa[a][0] = t->v;
for(int k = 1; k <= 15; k ++) fa[a][k] = fa[fa[a][k - 1]][k - 1];
dfs(t->right,d + 1);
}
}
}
int lca(int a,int b)
{
if(dis[a] < dis[b]) swap(a,b);
for(int k = 15; k >= 0; k --)
if(dis[fa[a][k]] >= dis[b]) a = fa[a][k];
if(a == b) return a;
for(int k = 15; k >= 0; k --)
if(fa[a][k] != fa[b][k]) a = fa[a][k],b = fa[b][k];
return fa[a][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i ++) scanf("%d",&iner[i]);
for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
tr = build(0,m - 1,0,m - 1);
memset(dis,0x3f,sizeof dis);
dis[0] = 0;dis[pre[0]] = 1;
dfs(tr,1);
for(int i = 0; i < n; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
if(se.count(a) && !se.count(b)) printf("ERROR: %d is not found.\n",b);
else if(!se.count(a) && se.count(b)) printf("ERROR: %d is not found.\n",a);
else if(!se.count(a) && !se.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
else
{
int t = lca(a,b);
if(t == a) printf("%d is an ancestor of %d.\n",a,b);
else if(t == b) printf("%d is an ancestor of %d.\n",b,a);
else printf("LCA of %d and %d is %d.\n",a,b,t);
}
}
return 0;
}
法2:也是稳妥做法,防止权值过大,或出现负权值
#include <iostream>
#include <unordered_map>
#include <cstring>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N];
struct node
{
int v;
node*left,*right,*parent;
node(int v) : v(v),left(NULL),right(NULL),parent(NULL){}
}*tr;
unordered_map<int,node*>mp;
node *build(int preL,int preR,int inerL,int inerR)//前中建树
{
if(preL > preR) return NULL;
int k = inerL;
node* t = new node(pre[preL]);
while(pre[preL] != iner[k]) k ++;//前序第一个结点就是根,在中序里面找根
t -> left = build(preL + 1,preL + k - inerL,inerL,k - 1);
t -> right = build(preL + k - inerL + 1,preR,k + 1,inerR);
if(t -> left != NULL) t -> left -> parent = t;
if(t -> right != NULL) t -> right -> parent = t;
mp[pre[preL]] = t;
return t;
}
int depth(node *a)
{
int res = 0;
while(a -> parent != NULL) a = a -> parent,res ++;
return res;
}
int lca(node *a,node *b)
{
int d1 = depth(a),d2 = depth(b);
while(d1 > d2) a = a -> parent,d1 --;//走到同一深度
while(d1 < d2) b = b -> parent,d2 --;
if(a == b) return a -> v;
while(a != b) a = a -> parent,b = b -> parent,d1 --,d2 --;//一起向上走
return a -> v;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i ++) scanf("%d",&iner[i]);
for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
tr = build(0,m - 1,0,m - 1);
for(int i = 0; i < n; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
if(mp.count(a) && !mp.count(b)) printf("ERROR: %d is not found.\n",b);
else if(!mp.count(a) && mp.count(b)) printf("ERROR: %d is not found.\n",a);
else if(!mp.count(a) && !mp.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
else
{
int t = lca(mp[a],mp[b]);
if(t == a) printf("%d is an ancestor of %d.\n",a,b);
else if(t == b) printf("%d is an ancestor of %d.\n",b,a);
else printf("LCA of %d and %d is %d.\n",a,b,t);
}
}
return 0;
}
参考了柳神的代码,发现不需要建树,直接递归求解代码还非常精简。
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N];
unordered_map<int,int>mp;
void lca(int inL,int inR,int u,int a,int b)
{
if(inL > inR) return ;
int l = mp[a],r = mp[b],root = mp[pre[u]];
if(l < root && r < root) lca(inL,root - 1,u + 1,a,b); //a和b都在当前子树根的左边
else if(l > root && r > root) lca(root + 1,inR,u + root - inL + 1,a,b);//a和b都在当前子树根的右边
else if (l < root && r > root || l > root && r < root)//分布在左右的话,root就是最近公共祖先
printf("LCA of %d and %d is %d.\n",a,b,iner[root]);
else if(l == root)
printf("%d is an ancestor of %d.\n",a,b);
else printf("%d is an ancestor of %d.\n",b,a);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i ++)
{
scanf("%d",&iner[i]);
mp[iner[i]] = i;//存位置
}
for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
for(int i = 0; i < n; i ++)
{
int a,b;
scanf("%d%d",&a,&b);
if(mp.count(a) && !mp.count(b)) printf("ERROR: %d is not found.\n",b);
else if(!mp.count(a) && mp.count(b)) printf("ERROR: %d is not found.\n",a);
else if(!mp.count(a) && !mp.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
else lca(0,m - 1,0,a,b);
}
return 0;
}