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语句判定即可,只是本题需要注意输入输出而已