[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 ...
随机推荐
- elasticsearch java索引的增删改查
1.创建索引并插入数据 Map<String, Object> json = new HashMap<String, Object>(); json.put("use ...
- Flutter点击两次返回键退出APP
在APP中一些页面为了防止用户操作失误点击到返回键导致退出APP,可以设置其一定时间内点击两次返回键才允许退出APP,完成这个功能可以通过WillPopScope和SystemNavigator.po ...
- express利用nodemailer发送邮件(163邮箱)
Nodemailer 是一个简单易用的Node.js邮件发送组件 首先安装这个组件 npm install nodemailer --save 安装之后,可以在某个get请求下,发送邮件,具体路由代码 ...
- Mac下安装MySQL 5.6.40
Mac下安装MySQL 5.6.40 - 干勾鱼的CSDN博客 - CSDN博客 原创 Mac下安装MySQL 5.6.40 2018-06-18 10:34:03 我是干勾鱼 阅读数 5927 ...
- C++ STL partial_sort_copy greater
#include <iostream>#include <deque>#include <algorithm>#include <vector>#inc ...
- 19 Flutter仿京东商城项目 商品详情 底部浮动导航布局 商品页面布局
效果: widget/JdButton.dart import 'package:flutter/material.dart'; import '../services/ScreenAdaper.da ...
- LODOP直接导出图片不弹框
之前有博文测试了导出图片的图片长度关系,是直接弹窗的选择保存路径的方式:Lodop导出图片,导出单页内容的图片最近测试下不弹窗保存图片是否可以,样例是保存的excel,测试了下图片,图片也是可以的,该 ...
- MUI和html5plus
MUI: http://dev.dcloud.net.cn/mui/snippet/ html5plus: http://www.html5plus.org/doc/h5p.html
- ID3算法(MATLAB)
ID3算法是一种贪心算法,用来构造决策树.ID3算法起源于概念学习系统(CLS),以信息熵的下降速度为选取测试属性的标准,即在每个节点选取还尚未被用来划分的具有最高信息增益的属性作为划分标准,然后继续 ...
- Flutter安卓客户端打包
想要安装到手机上,必须要进行打包,因为没有苹果手机,所以只能打包Android客户端的apk. 检查 App的配置 查看默认应用程序清单文件(位于/android/app/src/main/中的And ...