这篇介绍如何用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算法求无向图的双连通分量的更多相关文章

  1. POJ 3352 无向图边双连通分量,缩点,无重边

    为什么写这道题还是因为昨天多校的第二题,是道图论,HDU 4612. 当时拿到题目的时候就知道是道模版题,但是苦于图论太弱.模版都太水,居然找不到. 虽然比赛的时候最后水过了,但是那个模版看的还是一知 ...

  2. tarjan算法求无向图的桥、边双连通分量并缩点

    // tarjan算法求无向图的桥.边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> ...

  3. Expm 9_3 无向图的双连通分量问题

      [问题描述] 给定一个无向图,设计一个算法,判断该图中是否存在关节点,并划分双连通分量. package org.xiu68.exp.exp9; import java.util.Stack; p ...

  4. [Tarjan系列] Tarjan算法求无向图的桥和割点

    RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...

  5. tarjan算法--求无向图的割点和桥

    一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中 ...

  6. [Tarjan系列] Tarjan算法与有向图的SCC

    前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...

  7. tarjan算法应用 割点 桥 双连通分量

    tarjan算法的应用. 还需多练习--.遇上题目还是容易傻住 对于tarjan算法中使用到的Dfn和Low数组. low[u]:=min(low[u],dfn[v])--(u,v)为后向边,v不是u ...

  8. UVALive 3523 Knights of the Round Table 圆桌骑士 (无向图点双连通分量)

    由于互相憎恨的骑士不能相邻,把可以相邻的骑士连上无向边,会议要求是奇数,问题就是求不在任意一个简单奇圈上的结点个数. 如果不是二分图,一定存在一个奇圈,同一个双连通分量中其它点一定可以加入奇圈.很明显 ...

  9. poj 2942 Knights of the Round Table(无向图的双连通分量+二分图判定)

    #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #includ ...

随机推荐

  1. Qt 线程基础(QThread、QtConcurrent等) 2

    使用线程 基本上有种使用线程的场合: 通过利用处理器的多个核使处理速度更快. 为保持GUI线程或其他高实时性线程的响应,将耗时的操作或阻塞的调用移到其他线程. 何时使用其他技术替代线程 开发人员使用线 ...

  2. 解决阿里云轻量级服务器mysql无法用数据库操作软件连接

    第一步:去阿里云购买一台轻量应用服务器Lamp然后登录到控制台点击应用详情 点击后你可以看到一些服务器的数据 首先是访问服务器的首页地址,默认会放一个html文件在网站根目录下(即/home/www/ ...

  3. 网络时间协议 (SNTP)

    sntp是简单网络时间协议(Simple Network Protocol)的客户端,可以用来查询或修正NTP服务器的时间和本地的时差. sntp可以以非交互模式运行或运行一个计划任务的脚本. snt ...

  4. VBA 格式化excel数据表 (数据分列)

    Sub ImportData() ' ' Copy Data from one workbook to the Current Workbook ' Place the macro file in t ...

  5. 002-log-log4j

    一.概述 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护 ...

  6. smart_pointer example

    #pragma oncetemplate<typename T>class smart_pointer{private: T* m_pRawPointer;public: smart_po ...

  7. bs4笔记

    1.网页输出乱码的解决办法 r= requests.get('https://www.baidu.com/') r.encoding = 'gbk2312'   #有可能 gbk.utf-8 soup ...

  8. 跨域form下载方式 批量下载

    downloadFileForm:function(fid) { var url = "https://file.xxxx.com/fileDownload.do"; var in ...

  9. Jenkins+gitlab+maven持续集成

    https://blog.csdn.net/tq08g2z/article/details/79718425 https://www.jianshu.com/p/3507d8b2ac87 报错用下面解 ...

  10. postman使用当前时间戳

    //设置当前时间戳postman.setGlobalVariable(“time”,Math.round(new Date().getTime()));time = postman.getGlobal ...