[Tarjan系列] Tarjan算法求无向图的双连通分量
这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量。
双联通分量包括点双连通分量v-DCC和边连通分量e-DCC。
若一张无向连通图不存在割点,则称它为“点双连通图”,不存在桥则称为“边双连通图”。
无向图的极大点双连通子图就v-DCC,极大边双连通子图就是e-DCC。
上一篇我们讲了如何用Tarjan算法求出无向图中的所有割点和桥。
不会求的朋友们可以去看一看上篇文章:Tarjan算法求无向图的割点和桥
这里“极大”的定义可以理解为包含部分点的最大的双连通子图,即不存在比包含它且比它更大的双连通子图。
下面给出几个定理:
1. 一张无向连通图是点双连通图当且仅当 图的顶点数<=2 or 图中任意两点都同时包含在至少一个简单环中。
2. 一张无向连通图是边双连通图当且仅当任意一条边都包含在至少一个简单换中。
接下来讲求法:
e-DCC的求法很简单,通过一遍Tarjan算法找到所有的桥,把桥删除后,无向图会分裂成一个个连通块。
每一个连通块都是一个e-DCC。
具体实现就是先用Tarjan算法标记所有桥,然后对整张图dfs一遍(不访问桥边),划分出所有连通块。
一般可以用一个数组表示每个节点所在的e-DCC的编号。
代码如下:
#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int head[N],tot=,n,m,cnt,dfn[N],low[N],c[N],bridge[N],dcc;
//c[x]储存x所在的e-DCC的编号,dcc存e-DCC的数量
inline void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x,int in_edge){
dfn[x]=low[x]=++cnt;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])
bridge[i]=bridge[i^]=;
}else if(i!=(in_edge^))
low[x]=min(low[x],dfn[y]);
}
}//Tarjan标记桥
void dfs(int x){
c[x]=dcc;//标号
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(c[y]||bridge[i])continue;//如果已经有标号了或者这条边是桥就不访问
dfs(y);
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])tarjan(i,);
for(int i=;i<=n;i++){
if(!c[i]){
++dcc;dfs(i);//每个联通块都进去标号
}
}
for(int i=;i<=n;i++)
printf("%d %d\n",i,c[i]);
return ;
}
接下来讲v-DCC的求法。
v-DCC是一个很容易混淆的概念。
由于v-DCC定义中的“极大”,一个割点可能属于多个v-DCC。
为了求出v-DCC,我们需要在Tarjan的过程中维护一个栈。
当一个点第一次被访问时,我们将它入栈。而当割点判定法则成立时,无论x是否为根,都要
从栈顶不断弹出节点直到y节点被弹出,这些被弹出的节点包括x节点一起构成一个v-DCC。
听上去挺简单的,实际上代码也很好写。
#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int head[N],tot=,n,m,rt,dfn[N],low[N],cnt,stk[N],top,num,cut[N];
vector<int> dcc[N];
inline void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt;
stk[++top]=x;//第一次访问该节点,入栈
if(x==rt && head[x]==){//判断孤立点,直接插入vector
dcc[++num].push_back(x);
return;
}
int flag=;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){//割点判定法则
flag++;
if(x!=rt||flag>)cut[x]=;
num++;int z;//根据上面描述的做法,把所有栈中的节点插入vector
do{
z=stk[top--];
dcc[num].push_back(z);//全部插入
}while(z!=y);
dcc[num].push_back(x);//包括x自己
}
}else low[x]=min(low[x],dfn[y]);//不在搜索树上,继续更新low
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=;i<=num;i++){
printf("%d:",i);//第i个v-DCC
for(int j=;j<dcc[i].size();j++)
printf(" %d",dcc[i][j]);//第i个v-DCC中所有的点
putchar('\n');
}
return ;
}
那么这一篇就讲到这里了,下一篇写一篇短博客更新e-DCC和v-DCC的缩点。
[Tarjan系列] Tarjan算法求无向图的双连通分量的更多相关文章
- POJ 3352 无向图边双连通分量,缩点,无重边
为什么写这道题还是因为昨天多校的第二题,是道图论,HDU 4612. 当时拿到题目的时候就知道是道模版题,但是苦于图论太弱.模版都太水,居然找不到. 虽然比赛的时候最后水过了,但是那个模版看的还是一知 ...
- tarjan算法求无向图的桥、边双连通分量并缩点
// tarjan算法求无向图的桥.边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> ...
- Expm 9_3 无向图的双连通分量问题
[问题描述] 给定一个无向图,设计一个算法,判断该图中是否存在关节点,并划分双连通分量. package org.xiu68.exp.exp9; import java.util.Stack; p ...
- [Tarjan系列] Tarjan算法求无向图的桥和割点
RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...
- tarjan算法--求无向图的割点和桥
一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中 ...
- [Tarjan系列] Tarjan算法与有向图的SCC
前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...
- tarjan算法应用 割点 桥 双连通分量
tarjan算法的应用. 还需多练习--.遇上题目还是容易傻住 对于tarjan算法中使用到的Dfn和Low数组. low[u]:=min(low[u],dfn[v])--(u,v)为后向边,v不是u ...
- UVALive 3523 Knights of the Round Table 圆桌骑士 (无向图点双连通分量)
由于互相憎恨的骑士不能相邻,把可以相邻的骑士连上无向边,会议要求是奇数,问题就是求不在任意一个简单奇圈上的结点个数. 如果不是二分图,一定存在一个奇圈,同一个双连通分量中其它点一定可以加入奇圈.很明显 ...
- poj 2942 Knights of the Round Table(无向图的双连通分量+二分图判定)
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #includ ...
随机推荐
- 表单 Flask-WTF - 校验器
1 wtforms内置的校验器 Class wtforms.validators.DataRequired(message=None)此验证器将会检测field是否输入了数值,实际上是进行了if fi ...
- ORM SQLAlchemy - 建立一个关系 relationship
relationship函数是sqlalchemy对关系之间提供的一种便利的调用方式, backref参数则对关系提供反向引用的声明 1 背景 如没有relationship,我们只能像下面这样调用关 ...
- Netty实践场景
数据通信 如果需要考虑的是两台机器(甚至多台)怎么使用Netty进行通信.大体上分为三种: 1 第一种:使用长连接通道不断开的形式进行通信.也就是服务端和客户端的通道一直处于开启状态. 如果服务器性能 ...
- ArcGIS超级工具SPTOOLS-线封闭,点集转面
一.线封闭 操作视频:https://weibo.com/tv/v/HvyvbAxKh?fid=1034:4375207666991674 将末端不闭合线,自动生成闭合的线,效果如下 原始线:末端不闭 ...
- C# Under the Hood: async/await (Marko Papic)
https://www.markopapic.com/csharp-under-the-hood-async-await/ Async and await keywords came with C# ...
- django models 关系
1.一对多/多对一 class Entry(models.Model): name=models.CharField(max_length=50) def __str__(self): return ...
- MySQL中information_schema数据库是干啥的
大家在安装或使用MYSQL时,会发现除了自己安装的数据库以外,还有一个 information_schema数据库.information_schema数据库是做什么用的呢,使用WordPress博客 ...
- [drf]源码和序列化梳理
drf源码继承管理 # drf继承关系 View APIView as_view: 执行父类的as_view 调用dispatch dispatch init_request request.quer ...
- LC 988. Smallest String Starting From Leaf
Given the root of a binary tree, each node has a value from 0 to 25 representing the letters 'a' to ...
- Handler常见两种用法
1.Handler在Android的两个功能 1.1表示未来某时做某事 1.2线程间通信 2.演示源码如下: package com.example.datastrorage; import andr ...