博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tarjan求点双连通分量
阅读量:5022 次
发布时间:2019-06-12

本文共 2717 字,大约阅读时间需要 9 分钟。

概述

在一个无向图中,若任意两点间至少存在两条“点不重复”的路径,则说这个图是点双连通的(简称双连通,biconnected)

在一个无向图中,点双连通的极大子图称为点双连通分量(简称双连通分量,Biconnected Component,BCC)

 

性质

  1. 任意两点间至少存在两条点不重复的路径等价于图中删去任意一个点都不会改变图的连通性,即BCC中无割点
  2. 若BCC间有公共点,则公共点为原图的割点
  3. 无向连通图中割点一定属于至少两个BCC,非割点只属于一个BCC

 

算法

 在Tarjan过程中维护一个栈,每次Tarjan到一个结点就将该结点入栈,回溯时若目标结点low值不小于当前结点dfn值就出栈直到目标结点(目标结点也出栈),将出栈结点和当前结点存入BCC

(说实话我觉得存点不比存边难理解和实现啊……下面会解释)

 

理解

首先申明一下,在我找到的BCC资料中,在算法实现中均将两个点和一条边构成的图称为BCC,此文章也沿用此的规定

如下图:

我猜想可能是因为割点的定义,此图中两个点均不为割点,所以此图也属于BCC?

总之做题时注意题面要求,若要求的不含此种BCC则判断每个BCC的大小即可

无向连通图中割点一定属于至少两个BCC,非割点只属于一个BCC

有了上面的规定我们也不难理解这一条了:割点就算相邻也会属于至少两个BCC;BCC间的交点都是割点,所以非割点只属于一个BCC

到一个结点就将该结点入栈

为什么用栈存储呢?因为DFS是由上到下的,而分离BCC是自下而上的,需要后进先出的数据结构——栈

回溯时若目标结点low值不小于当前结点dfn值就出栈直到目标结点(目标结点也出栈),将出栈结点和当前结点存入BCC

对于每个BCC,它在DFS树中最先被发现的点一定是割点或DFS树的树根

证明:割点是BCC间的交点,故割点在BCC的边缘,且BCC间通过割点连接,所以BCC在DFS树中最先被发现的点是割点;特殊情况是对于开始DFS的点属于的BCC,其最先被发现的点就是DFS树的树根

上面的结论等价于每个BCC都在其最先被发现的点(一个割点或树根)的子树中

 这样每发现一个BCC(low[v]>=dfn[u]),就将该子树出栈,并将该子树和当前结点(割点或树根)加入BCC中。上面的操作与此描述等价

(我就是因为这个条件“将子树出栈”没理解写错了结果调了一晚上poj2942)

 

综上,存点是不是很好理解?存边虽然不会涉及重复问题(割点属于至少两个BCC),但会有很多无用操作。个人觉得存点也是个不错的选择。

 

模板

并没有模板题

1 #include
2 #include
3 #include
4 using namespace std; 5 struct edge 6 { 7 int to,pre; 8 }edges[1000001]; 9 int head[1000001],dfn[1000001],dfs_clock,tot;10 int num;//BCC数量 11 int stack[1000001],top;//栈 12 vector
bcc[1000001];13 int tarjan(int u,int fa)14 {15 int lowu=dfn[u]=++dfs_clock;16 for(int i=head[u];i;i=edges[i].pre)17 if(!dfn[edges[i].to])18 {19 stack[++top]=edges[i].to;//搜索到的点入栈 20 int lowv=tarjan(edges[i].to,u);21 lowu=min(lowu,lowv);22 if(lowv>=dfn[u])//是割点或根 23 {24 num++;25 while(stack[top]!=edges[i].to)//将点出栈直到目标点 26 bcc[num].push_back(stack[top--]);27 bcc[num].push_back(stack[top--]);//目标点出栈 28 bcc[num].push_back(u);//不要忘了将当前点存入bcc 29 }30 }31 else if(edges[i].to!=fa)32 lowu=min(lowu,dfn[edges[i].to]);33 return lowu;34 }35 void add(int x,int y)//邻接表存边 36 {37 edges[++tot].to=y;38 edges[tot].pre=head[x];39 head[x]=tot;40 }41 int main()42 {43 int n,m;44 scanf("%d%d",&n,&m);45 for(int i=1;i<=m;i++)46 {47 int x,y;48 scanf("%d%d",&x,&y);49 add(x,y),add(y,x);50 }51 for(int i=1;i<=n;i++)//遍历n个点tarjan 52 if(!dfn[i])53 {54 stack[top=1]=i;55 tarjan(i,i);56 }57 for(int i=1;i<=num;i++)58 {59 printf("BCC#%d: ",i);60 for(int j=0;j
双连通分量

 

转载于:https://www.cnblogs.com/LiHaozhe/p/9527136.html

你可能感兴趣的文章
luogu1373 小a和uim之大逃离 (dp)
查看>>
Redis的Pub/Sub客户端实现
查看>>
SQL日常问题和技巧——持续更新
查看>>
springMVC入门(一)------springMVC基本概念与安装
查看>>
Sam做题记录
查看>>
[bzoj] 2453 维护数列 || 单点修改分块
查看>>
IIS版本变迁
查看>>
BZOJ3884: 上帝与集合的正确用法 拓展欧拉定理
查看>>
mybatis09--自连接一对多查询
查看>>
myeclipse10添加jQuery自动提示的方法
查看>>
【eclipse jar包】在编写java代码时,为方便编程,常常会引用别人已经实现的方法,通常会封装成jar包,我们在编写时,只需引入到Eclipse中即可。...
查看>>
视频监控 封装[PlayCtrl.dll]的API
查看>>
软件工程APP进度更新
查看>>
Python 使用正则替换 re.sub
查看>>
CTF中那些脑洞大开的编码和加密
查看>>
简化工作流程 10款必备的HTML5开发工具
查看>>
c++ 调用外部程序exe-ShellExecuteEx
查看>>
Java进击C#——语法之知识点的改进
查看>>
IdentityServer流程图与相关术语
查看>>
BirdNet: a 3D Object Detection Framework from LiDAR information
查看>>