Tarjan求图的割点、桥与双连通分量

来源:互联网 发布:淘宝图片空间网址 编辑:程序博客网 时间:2024/06/05 02:37

关于Tarjan的部分知识:poj1236:Network of School(Tarjan)

  • 图的割点桥与双连通分量
    • 定义
      • 点连通度与边连通度
      • 双连通图割点与桥
      • 双连通分量分支
    • Tarjan算法
    • 求双连通分量
      • 例题

图的割点、桥与双连通分量

定义

点连通度与边连通度:

在一个无向连通图中,如果有一个顶点集合v,删除顶点集合v,以及与v中顶点相连 (至少有一端在v中)的所有边后,原图不连通,就称这个点集v为割点集合。

一个图的点连通度的定义为:最小割点集合中的顶点数。

类似的,如果有一个边集合,删除这个边集合以后,原图不连通,就称这个点集为割边 集合。

一个图的边连通度的定义为:最小割边集合中的边数。

双连通图、割点与桥:

如果一个无向连通图的点连通度大于1,则称该图是点双连通的 (point biconnected),简 称双连通或重连通。一个图有割点,当且仅当这个图的点连通度为1,则割点集合的唯一元素被称为割点(cut point),又叫关节点(articulationpoint)。一个图可能有多个割点。

如果一个无向连通图的边连通度大于1,则称该图是边双连通的 (edge biconnected),简 称双连通或重连通。一个图有桥,当且仅当这个图的边连通度为 ,则割边集合的唯一元素 被称为桥(bridge),又叫关节边(articulationedge)。一个图可能有多个桥。

可以看出,点双连通与边双连通都可以简称为双连通,它们之间是有着某种联系的,下 文中提到的双连通,均既可指点双连通,又可指边双连通。(但这并不意味着它们等价)。

双连通分量(分支)

在图G的所有子图G’中,如果G’是双连通的,则称G’为双连通 子图。如果一个双连通子图G’它不是任何一个双连通子图的真子集,则 为极大双连通子 图。双连通分量(biconnectedcomponent),或重连通分量,就是图的极大双连通子图。特殊的,点双连通分量又叫做块。

Tarjan算法

与有向图求强连通分量的Tarjan算法类似,只需通过求dfn值与low值来得出割点与桥。
对图深度优先搜索(DFS),定义dfn(u)为u在搜索树 (以下简称为树)中被遍历到的次序号。定义low(u)为u或u子树中的结点经过最多一条后向边能追溯到的最早的树中结点次序号(注意:与DAG不同的是,这里的后向边不包括与搜索树中父亲的连边)。
根据定义,则有:
low(u)=Min{}dfn(u)dfn(v)(u,v)low(v)(u,v)
一个顶点u是割点,当且仅当满足 (1)或(2):
(1)u为树根,且u有多于一个子树。因为无向图DFS搜索树中不存在横叉边,所以若有多个子树,这些子树间不会有边相连。
(2)u不为树根,且满足存在(u,v)为树枝边 (即 为 在搜索树中的父亲),并使得 DFN(u)<=Low(v).(因为删去 后 以及 的子树不能到达 的其他子树以及祖先)。

实现时,因为有重边的问题,所以需要将一条无向边拆为两条编号一样的有向边,用邻 接表进行存储。在判断 是否为后向边时要注意是树枝边的反向边还是新的一条反向边。

求双连通分量

下面要分开讨论点双连通分量与边双连通分量的求法。

对于点双连通分量,实际上在求割点的过程中就能顺便把每个点双连通分支求量。建立 一个栈,存储当前双连通分量,在搜索图时,每找到一条树枝边或后向边(非横叉边),就 把这条边加入栈中。如果遇到某时满足 DFN(u)<=Low(v) 说明u是一个割点,同时把边从栈 顶一个个取出,直到遇到了边(u,v) ,取出的这些边与其相连的点,组成一个点双连通分量。割点可以属于多个点双连通分量,其余点和每条边只属于且属于一个点双连通分量。
(这里选择储存边而不是储存点是因为一个割点可以属于多个点双连通分量)

对于边双连通分量,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分量。桥不属于任何一个边双连通分量,其 余的边和每个顶点都属于且只属于一个边双连通分量。可以用并查集实现。 (一定注意考虑重边的可能性)

一个有桥的连通图,如何把它通过加边变成边双连通图?

方法为首先求出所有的桥,然 后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。
统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf 。则至少在树上添加 (leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。
(证明略,请读者感性思考。。。)

例题

1.割点的相关题:poj1523:SPF
2.将图转化为双连通图:poj3177:Redundant Paths(注意重边)

下面给出两题AC代码,仅供参考。

  • Problem 1 Code:
#include<iostream>#include<cstdio>using namespace std;const int Maxn=1e3+50;const int Maxm=2e6+50;inline int read(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}int n,low[Maxn],dfn[Maxn],from[Maxn],ind,rt;int before[Maxm],last[Maxn],to[Maxm],ecnt,fg[Maxn];inline void add(int x,int y){    ++ecnt;    before[ecnt]=last[x];    last[x]=ecnt;    to[ecnt]=y;}inline void dfs(int now){    dfn[now]=low[now]=++ind;    int bz=0;    for(int e=last[now];e;e=before[e])    {        int v=to[e];        if(!dfn[v])        {            bz++;            from[v]=e;            dfs(v);            low[now]=min(low[now],low[v]);            if(rt!=now&&low[v]>=dfn[now])fg[now]++;        }        else if(from[now]!=(e^1))        {            low[now]=min(low[now],dfn[v]);        }        if(rt==now&&bz>=2)fg[now]=bz-1;    }}int s=0;int main(){    while(1)    {        if(s)printf("\n");        ++s;        n=ind=0;        ecnt=1;        memset(last,0,sizeof(last));        memset(from,0,sizeof(from));        memset(dfn,0,sizeof(dfn));        memset(fg,0,sizeof(fg));        int x,y;        while(x=read(),x)        {            y=read();            n=max(n,max(x,y));            add(x,y);            add(y,x);        }        if(!n)break;        printf("Network #%d\n",s);        for(int i=1;i<=n;i++)if(!dfn[i])rt=i,dfs(i);        int d=0;        for(int i=1;i<=n;i++)        {            if(fg[i])d=1,printf("  SPF node %d leaves %d subnets\n",i,fg[i]+1);        }        if(!d)printf("  No SPF nodes\n");    }}
  • Problem 2 Code:
#include<iostream>#include<cstdio>#include<cmath>#include<vector>using namespace std;const int Maxn=5e3+50;const int Maxm=2e4+50;inline int read(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f;}int n,m,low[Maxn],dfn[Maxn],ind,cnt,fa[Maxn],vis[Maxn],in[Maxn],from[Maxn];int last[Maxn],before[Maxm],to[Maxm],ecnt=1,cut[Maxm],st[Maxm];inline void add(int x,int y){    ecnt++;    st[ecnt]=x;    before[ecnt]=last[x];    last[x]=ecnt;    to[ecnt]=y;}inline void dfs(int now){    dfn[now]=low[now]=++ind;    for(int e=last[now];e;e=before[e])    {        int v=to[e];        if(!dfn[v])        {            from[v]=e;            dfs(v);            low[now]=min(low[now],low[v]);        }        else if(from[now]!=(e^1))        {            low[now]=min(low[now],dfn[v]);        }        if(low[v]>dfn[now])        {            cut[e]=cut[e^1]=1;        }    }}inline int getf(int x){    if(fa[x]==x)return x;    return fa[x]=getf(fa[x]);}int main(){    n=read(),m=read();    for(int i=1;i<=m;i++)    {        int x=read(),y=read();        add(x,y);        add(y,x);    }    dfs(1);    for(int i=1;i<=n;i++)fa[i]=i;    for(int e=2;e<=ecnt;e+=2)        if(!cut[e])fa[getf(st[e])]=getf(to[e]);    for(int e=2;e<=ecnt;e+=2)        if(cut[e])in[getf(to[e])]++,in[getf(st[e])]++;    for(int i=1;i<=n;i++)    {        if(in[getf(i)]==1&&getf(i)==i)++cnt;    }    printf("%d\n",(cnt+1)/2);    return 0;}
原创粉丝点击