[学习笔记] Tarjan算法求桥和割点

在之前的博客中我们已经介绍了如何用Tarjan算法求有向图中的强连通分量,而今天我们要谈的Tarjan求桥、割点,也是和上篇有博客有类似之处的。

关于桥和割点:

桥:在一个有向图中,如果删去一条边,而后这个有向图不再联通,我们便称删去的这条边为有向图的桥。

割点:在一个有向图中,如果删去一个点,使这个有向图中剩下的点不在联通,我们便称这个点为有向图的割点。

Tarjan算法原理分析:

和上文一样的,我们求出一个dfn数组(进行dfs时遍历的顺序),和一个low数组(以u为根的子树中,能连到dfn值最小的节点)。

求桥:

对于一个条边u,v(u,v为边的两个端点),如果dfn[u]<=low[v],那么这条边就是桥。因为以v为根的子树中无法通过返祖边(返祖边的定义可以参考我之前的博客)来连到u的祖先,只能通过u,v这条边才能与u的祖先联通,那么也就意味着将着条边删去后,以v为根的子树将不再能与其他点联通,所以这条边就是桥。

求割点:

对于一个点u:

                  1、如果它是根,那么假如它只有一个儿子,那么它就不是割点,假如有多个儿子,那么它就是割点。

                  2、如果它不是根,那么假如dfn[u]<=low[v],它就是割点。因为删掉点u以后,v以及v的子树不能到达u的祖先。

特别注意:     

在无向图中,不一定需要返祖边来更新low数组,横叉边也一样可以。

代码:

var
  next,head,vet,a,dfn,low:array[1..200000]of longint;
  ans,i,n,m,x,y,tot,time,root:longint;
function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;
procedure add(x,y:longint);
begin
  inc(tot);
  next[tot]:=head[x];
  vet[tot]:=y;
  head[x]:=tot;
end;
procedure tarjan(u:longint);
var
  i,v,cnt:longint;
begin                                 //
  inc(time); i:=head[u]; cnt:=0;
  dfn[u]:=time; low[u]:=time;
  while i<>0 do
  begin
    v:=vet[i];
    if dfn[v]>0 then low[u]:=min(dfn[v],low[u]) else
    begin
      inc(cnt);
      tarjan(v);
      low[u]:=min(low[u],low[v]);
      if ((u<>root)and(dfn[u]<=low[v])or(u=root)and(cnt>1))and(a[u]=0) then
      begin
        inc(ans); a[u]:=1;
      end;
    end;
    i:=next[i];
  end;
end;
begin
  read(n,m);
  for i:=1 to m do
  begin
    read(x,y);
    add(x,y); add(y,x);
  end;
  for i:=1 to n do
  begin
    root:=i;
    if dfn[i]=0 then tarjan(i);
  end;
  writeln(ans);
  for i:=1 to n do
    if a[i]=1 then write(i,' ');
  writeln;
end.
原文地址:https://www.cnblogs.com/WR-Eternity/p/9772279.html