您的位置:首页 > 编程语言 > C语言/C++

求LCA最近公共祖先的在线ST算法_C++

2016-09-03 16:57 585 查看
  ST算法是求最近公共祖先的一种 在线 算法,基于RMQ算法,本代码用双链树存树

  预处理的时间复杂度是 O(nlog2n) 查询时间是 O(1) 的

  另附上离线算法 Tarjan 的链接:

    http://www.cnblogs.com/hadilo/p/5840390.html

  首先预处理出深度,以及 DFS 序,这里的DFS序是指回溯时也算上,比如





void dfs(int x,int dep)
{
int i;
d[x]=dep;
a[++top]=x;
for (i=down[x];i!=0;i=next[i])
{
dfs(i,dep+1);
a[++top]=x;
}
}


  然后记录每个节点在 DFS 序中第一次出现的位置,b[i] 为第 i 号节点第一次出现的位置

for (i=1;i<=top;i++) if (b[a[i]]==0) b[a[i]]=i;


  开始 DP 处理区间区间内最小值,这里使用 RMQ 算法,其功能类似于线段树或树状数组

  f[i][j] 表示从第 i 位开始,连续 2j 个数的最小值,状态转移:

f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1])


  因为它是 2 的幂次方的状态,所以每次转移可以看做把当前状态分为两个相等的部分,求两部分的最小值

  如: 5 7 3 2 和 4 6 1 5

     min=2 min=1

 即 f[1][2]=2 f[5][2]=1

  所以 f[1][3]=min(f[1][2],f[5][2])=1

  初始状态:f[i][0]=d[a[i]] loc[i][0]=a[i]

  注意这里 f 记录的是它的深度的最小值,而位置用 loc 记录

void init()
{
int i,j,s,x,k;
for (i=1;i<=top;i++)
{
f[i][0]=d[a[i]];
loc[i][0]=a[i];
}
s=log2(top);
for (j=1;j<=s;j++)
{
k=top-(1<<j)+1;
for (i=1;i<=k;i++)
{
x=i+(1<<(j-1));
if (f[i][j-1]<=f[x][j-1])
{
f[i][j]=f[i][j-1];
loc[i][j]=loc[i][j-1];
}
else
{
f[i][j]=f[x][j-1];
loc[i][j]=loc[x][j-1];
}
}
}
}


  代码用变量优化了一下常数

  接着开始进行询问

  读入两个节点,查询它们第一次出现的位置

  在这两个位置之间的区间查询最小深度的节点,该节点即为最近公共祖先

  查询区间时,我们把它分成两个部分,可以有重叠,如

    8 9 6 5 6 8 4

  这7个节点,把它分成: 8 9 6 5 和 5 6 8 4

              min=5 min=4

  则最小值为 min(5,4)=4

min(f[x][log2(y-x)],f[y-(1<<i)+1][log2(y-x)]);


  可以这样理解:

    将两个位置的距离取个对数记为 i,然后从最左边,往后共 2i 个数的最小值,这是第一部分

    第二个部分是从右边往左推 2i 个数,即 y-2i+1,然后再往后取 2i 个数

  成功将区间分为两部分

scanf("%d",&t);
while (t>0)
{
t--;
scanf("%d%d",&x,&y);
x=b[x];
y=b[y];
if (x>y) swap(x,y);
i=log2(y-x);
k=y-(1<<i)+1;
printf("%d\n",f[x][i]<f[k][i]?loc[x][i]:loc[k][i]);
}


  代码内有常数优化,有的地方思路可能不是很清晰,请谅解

  给个完整代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 100001
using namespace std;

int a[N*2],d
,down
,next
,top,f[2*N][18],loc[2*N][18],n,b
;
int log2(int x)
{
int k=0;
while (x>1)
{
x/=2;
k++;
}
return k;
}
void dfs(int x,int dep)
{
int i;
d[x]=dep;
a[++top]=x;
for (i=down[x];i!=0;i=next[i])
{
dfs(i,dep+1);
a[++top]=x;
}
}
void init()
{
int i,j,s,x,k;
for (i=1;i<=top;i++)
{
f[i][0]=d[a[i]];
loc[i][0]=a[i];
}
s=log2(top);
for (j=1;j<=s;j++)
{
k=top-(1<<j)+1;
for (i=1;i<=k;i++)
{
x=i+(1<<(j-1));
if (f[i][j-1]<=f[x][j-1])
{
f[i][j]=f[i][j-1];
loc[i][j]=loc[i][j-1];
}
else
{
f[i][j]=f[x][j-1];
loc[i][j]=loc[x][j-1];
}
}
}
}
int main()
{
int i,k,x,y,t;
scanf("%d",&n);
for (i=1;i<=n;i++) down[i]=d[i]=next[i]=0;
for (i=1;i<=n;i++)
{
scanf("%d",&x);
next[i]=down[x];
down[x]=i;
}
top=0;
dfs(down[0],1);
for (i=1;i<=top;i++) if (b[a[i]]==0) b[a[i]]=i;
init();
scanf("%d",&t);
while (t>0)
{
t--;
scanf("%d%d",&x,&y);
x=b[x];
y=b[y];
if (x>y) swap(x,y);
i=log2(y-x);
k=y-(1<<i)+1;
printf("%d\n",f[x][i]<f[k][i]?loc[x][i]:loc[k][i]);
}
return 0;
}


版权所有,转载请联系作者,违者必究

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