Tarjan求点双连通分量

概述

在一个无向图中,若任意两点间至少存在两条“点不重复”的路径,则说这个图是点双连通的(简称双连通,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间的交点都是割点,所以非割点只属于一个BCC

回溯时若目标结点low值不小于当前结点dfn值就出栈直到当前结点(当前结点属于该BCC但不出栈)

一、当前结点为自下到上第一个(即高度最小的)割点

因为此结点是自下到上第一个,所以一定有至少一个孩子的low值=当前结点的dfn值

这个可以用反证法证明一下

low[v]=dfn[v]>dfn[u],那么v也是割点,这样u就不是高度最小的割点

所以满足条件的子树与割点构成一个双连通分量

二、当前结点为非根节点且不满足(一)

此部分BCC一定存在于割点之间

由于算法中的出栈操作已经将所有子树中最近(高度最高)的割点以下的结点全部出栈,所以我们直接进行算法中的出栈操作即都是割点间的结点(包括割点)

三、当前结点为根结点

1、当前结点只有一个子树,即当前结点不是割点,显然当前结点属于其孩子所在BCC

2、当前结点有多于一个子树,即当前结点是割点,则当前结点是其各个孩子所在BCC的交点,算法的正确性也很显然

综上,存点是不是很好理解?存边虽然不会涉及重复问题(割点属于至少两个BCC),但会有很多无用操作,写起来也稍麻烦些

模板

并没有模板题

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 #include<stack>
 5 using namespace std;
 6 struct edge
 7 {
 8     int to,pre;
 9 }edges[1000001];
10 int head[1000001],dfn[1000001],dfs_clock,tot;
11 int num;//BCC数量
12 stack<int>s;
13 vector<int>bcc[1000001];
14 int read()
15 {
16     int f=0,x=0;
17     char c=getchar();
18     while(!isdigit(c))
19     {
20         f=f|c==‘-‘;
21         c=getchar();
22     }
23     while(isdigit(c))
24     {
25         x=(x<<1)+(x<<3)+(c^48);
26         c=getchar();
27     }
28     return x;
29 }
30 int tarjan(int u,int fa)
31 {
32     int lowu=dfn[u]=++dfs_clock;
33     for(int i=head[u];i;i=edges[i].pre)
34         if(!dfn[edges[i].to])
35         {
36             s.push(edges[i].to);//搜索到的边入栈
37             int lowv=tarjan(edges[i].to,u);
38             lowu=min(lowu,lowv);
39             if(lowv>=dfn[u])//是割点或根
40             {
41                 num++;
42                 while(s.top()!=u)//将边出栈直到当前边
43                 {
44                     bcc[num].push_back(s.top());
45                     s.pop();
46                 }
47                 bcc[num].push_back(u);
48             }
49         }
50         else if(edges[i].to!=fa)
51             lowu=min(lowu,dfn[edges[i].to]);
52     return lowu;
53 }
54 void add(int x,int y)//邻接表存边
55 {
56     edges[++tot].to=y;
57     edges[tot].pre=head[x];
58     head[x]=tot;
59 }
60 int main()
61 {
62     int n,m;
63     scanf("%d%d",&n,&m);
64     for(int i=1;i<=m;i++)
65     {
66         int x,y;
67         scanf("%d%d",&x,&y);
68         add(x,y),add(y,x);
69     }
70     for(int i=1;i<=n;i++)//遍历n个点tarjan
71         if(!dfn[i])
72         {
73             s.push(i);
74             tarjan(i,i);
75         }
76     for(int i=1;i<=num;i++)
77     {
78         printf("BCC%d: ",i);
79         for(int j=0;j<bcc[i].size();j++)
80             printf("%d ",bcc[i][j]);
81         printf("\n");
82     }
83     return 0;
84 }

双连通分量模板

原文地址:https://www.cnblogs.com/LiHaozhe/p/9527136.html

时间: 2024-10-10 14:36:21

Tarjan求点双连通分量的相关文章

bzoj1123 BLO tarjan求点双连通分量

填坑--链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1123 题意:问切断第i个点之后多少对点不再联通. 就是个求割点同时计算出双连通分量大小嘛-- 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=10000

【POJ3352】Road Construction tarjan求边-双连通分量,裸题模板题

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42671851 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 裸题只给模板. tarjan可以实现. 太水不发题解. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 1010 #define M 202

【POJ1523】SPF tarjan求点-双连通分量 裸题模板题

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42671865 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 题意:求哪些点是割点,割掉以后能把图分成几块. 太水不欲发题解. tarjan就好,不懂看代码. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define

poj 3352 求 边-双连通分量

[题意] 给出一张无向连通图,求至少连几条边可以变成边双连通图 [思路]求出边-双连通分量,缩点就成了一棵树,求这棵树里的出度为1 的点num  结果是(num-1)/2; 1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #include<stack> 5 #include<vector> 6 using namespace std; 7 int pre[1002],

tarjan算法求桥双连通分量 POJ 3177 Redundant Paths

POJ 3177 Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12598   Accepted: 5330 Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the re

【BZOJ3331】[BeiJing2013]压力 Tarjan求点双

[BZOJ3331][BeiJing2013]压力 Description 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量.他们每天都生活在巨大的压力之下. 小强建立了一个模型.这世界上有N个网络设备,他们之间有M个双向的链接.这个世界是连通的.在一段时间里,有Q个数据包要从一个网络设备发送到另一个网络设备. 一个网络设备承受的压力有多大呢?很显然,这取决于Q个数据包各自走的路径.不过,某些数据包无论走什么路径都不可避免的要通过

图论算法-Tarjan模板 【缩点;割顶;双连通分量】

图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; bool ins[100010]; int col[100010];//记录每个点所属强连通分量(即染色) vector<int> map[100010]; stack<int> st; int tot;//时间戳 int colnum;//记录强连通分量个数 void tarjan(

POJ 3352 Road Construction(边双连通分量,桥,tarjan)

题解转自http://blog.csdn.net/lyy289065406/article/details/6762370   文中部分思路或定义模糊,重写的红色部分为修改过的. 大致题意: 某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,保证任意2个旅游景点之间有路径连通的(可间接连通).而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施. 道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行.然而有道路部门正在施工的道路,在施工完毕前是

fzu2181(点的双连通分量+求奇环)

求出每个点双连通分量,如果在一个点双连通分量中有奇环,则这个分量每个点都在一个奇环中.  关键是要知道怎么求点双连通分量以及点双连通的性质. fzu2181 http://acm.fzu.edu.cn/problem.php?pid=2181 #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define