无向图中的tarjan 算法及其割点、割边

Dfn数组记录搜索到该点的时间。

Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的Dfn值。

以上是有向图的low、dfn定义,实际上无向图与其类似。

在实际运用之前,先看下列一些定义:

割点:在一连通图内(无向),去掉一点,则该连通图不再连通,而化成数个子图,则该点被称为割点;,有时必须去掉两个或更多的点连通图才能不再连通,达到割点的效果,这些点被称为割点集合

割边:若删掉某边后,原连通图分裂为多个子图;如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合

一般来说,无向图中的tarjan主要围绕这两个问题进行,而判断割点和割边的方程可以通过low与dfn求出

割点的判定:

判定原理:

在一棵DFS树中

根root是割顶当且仅当它至少有两个儿子

其他点v是割顶当且仅当它有一个儿子u,从u或者u的后代出发没有指向v祖先(不含v)的B边, 则删除v以后u和v的父亲不连通, 故为割顶

割点判定算法:

对于DFS树根, 判断度数是否大于1

对于其他点u, 如果不是根的直接儿子,且low[u] >= dfn[P[u]], 则它的父亲v=P[u]是割点

割边的判定:

原理

发现T边(u,v)时若发现v和它的后代不存在一条连接u或其祖先的B边, 则删除(u,v)后u和v不连通, 因此(u,v)为桥

桥的判定算法

发现T边(u, v)时若low[v]>=dfn[u],则(u,v)为桥

注意:以上为无重边情况,当有重边时:

如果让发现该结点的边来更新该节点的low值,则必定会有low[u]=low[v]

所以不能让发现该结点的边来更新该节点的low值,在此条件下,dfn[v]=low[v]时,(u,v)是桥

例:

Poj1144

描述

一个电话线公司(简称TLC)正在建立一个新的电话线缆网络。他们连接了若干个地点分别从1到N编号。没有两个地点有相同的号码。这些线是双向的并且能使两个地点保持通讯。每个地点的线都终结于电话交换机。每个地点都有一个电话交换机。从每个地点都能通过线缆到达其他任意的地点,然而它并不需要直接连接,它可以通过若干个交换机来到达目的地。有时候某个地点供电出问题时,交换机就会停止工作。TLC的工作人员意识到,除非这个地点是不可达的,否则这种情况就会发生,它还会导致一些其它的地点不能互相通讯。在这种情况下我们会称这个地点(错误发生的地方)为critical。现在工作人员想要写一个程序找到所有critical地点的数量。帮帮他们。

 

输入

输入文件包括若组测试数据。每一组是一个网络,每一组测试数据的第一行是地点的总数量N<100.每个接下来最多N行包括一个数字表示一个地点和与它相连接的地点的数字。这些最多N行完全描述了整个网络,比如,网络中每个直接连接的两个地点被至少一行包括。一行内的所有数字都要用空格隔开。每组数据需要用单独的一个0结束。最后的块只有一行即N=0。

 

输出

输出除了最后一个组其他每一个组的critical地点的数量,每个块用一行输出。

 

样例输入:

5

5 1 2 3 4

0

6

2 1 3

5 4 6 2

0

0

 

样例输出

1

2

 

提示:

你需要确定每行的结束。为了方便判断,每行的结束都没有多余的空白

 

#include

#include

#include

using namespacestd;

const intMAXN=1000;

intfirst[MAXN],next[MAXN*MAXN],data[MAXN*MAXN],arcnum;

void add(int a,intb)

{

      arcnum++;

      data[arcnum]=b;

      next[arcnum]=first[a];

      first[a]=arcnum;

}

intlow[MAXN],dfn[MAXN],cnt=1,ans[MAXN],root;

void tarjan(int u)

{

      low[u]=dfn[u]=cnt++;

      int v,i,son=0;

      for(i=first[u];i!=0;i=next[i]){

           v=data[i];

           son++;

           if(dfn[v]==0){

                 tarjan(v);

                 if(low[u]>low[v])low[u]=low[v];

                 if((u==root&&son>=2)||(u!=root&&low[v]>=dfn[u]))ans[u]=1;

           }

           elseif(low[u]>dfn[v])low[u]=dfn[v];

      }

}

main()

{

      int n,i,num,a,b;

      char c;

      scanf("%d",&n);

      scanf("%c",&a);

      while(n!=0){

           memset(first,0,sizeof(first));

           memset(dfn,0,sizeof(dfn));

           memset(low,0,sizeof(low));

           memset(ans,0,sizeof(ans));

           scanf("%d",&a);

           while(a!=0)

           {

                 do{

                      scanf("%d",&b);

                      add(a,b);

                      add(b,a);

                      c=getchar();

                 }

                 while(c!='\n');

                 scanf("%d",&a);

           }

           root=1;

           tarjan(root);

           num=0;

           for(i=1;i<=n;i++){

                 if(ans[i]==1)num++;

           }

           printf("%d\n",num);

           scanf("%d",&n);

      }

     

}

运用上面的思路,用if语句判定即可,只是本题需要注意输入输出而已

你可能感兴趣的:(无向图中的tarjan 算法及其割点、割边)