一、基础

1、搜索树:在无向图中,我们以某一个节点 x 出发进行深度优先搜索,每一个节点只访问一次,所有被访问过的节点与边构成一棵树,称为无向连通图的搜索树

1、割点:若从图中删除节点x(以及所有与x关联的边之后),图将被分成不相连的子图,那么称 x 为图的割点

2、割边(桥):若从图中删除边e之后,图将分裂成两个不相连的子图,那么称e为图的割边或桥

3、时间戳:​用来标记图中每个节点在进行深度优先搜索时被访问的时间顺序,用 dfn[x] 来表示

4、追溯值:表示从当前节点x作为根节点出发,能够访问到的所有节点中,时间戳最小的值,用 low[x]来表示

计算追溯值:

(1)、先令low[x] = dfn[x]

(2)、若搜索树上x是y的父节点,则令low[x] = min(low[x], low[y])

(3)、若无向边(x,y)不是搜索树上的边,则令low[x] = min(low[x], dfn[y])

5、双连通分量:若一张无向连通图不存在割点,则称它为点双联通图,记为v-DCC;若一张无向连通图不存在桥,则称它为边双连通图,记为e-DCC

6、强连通分量:对于图中任意两个结点x、y,既存在从x到y的路径,也存在从y到x的路径,则称该图为强连通图;有向图的极大连通子图被称为强连通分量

7、Tarjan算法是基于深度优先搜索的算法,用于求解图的连通性问题。Tarjan算法可以在线性时间内求出无向图的割点与桥,进一步地可以求解无向图的双连通分量;同时,也可以求解有向图的强连通分量、必经点与必经边

二、求割边

一条边(u,v)是割边,当且仅当(u,v)为树枝边(即非负边),且满足:dfn(u)<low(v)(没有重边)

公式说明:从v的子树出发,在不经过(u, v)的前提下,不管走哪条路,都无法到达u或比u更早访问的结点。也就是说,u的儿子v之间只有一条边(无重边),且v点只能到u点到不了u点前

int dfn[maxn], low[maxn], bridge[maxn];
int to[maxn << 1], nex[maxn << 1], head[maxn];
int root, total, cnt;       //邻接表从2开始
void add(int u, int v)
{to[++cnt] = v;nex[cnt] = head[u];head[u] = cnt;
}
void Tarjan(int u, int father)
{dfn[u] = low[u] = ++total;for (int i = head[u]; i; i = nex[i]){int v = to[i];if (!dfn[v]){Tarjan(v, i);low[u] = min(low[u], low[v]);if (low[v] > dfn[u])bridge[i] = bridge[i ^ 1] = 1;    //记录路径}else if (i != (father ^ 1))         //判断重边{low[u] = min(low[u], dfn[v]);}}
}
void solve()
{int x, y, n, m;cin >> n >> m;cnt = 1;for (int i = 0; i < m; i++){cin >> x >> y;add(x, y);add(y, x);}for (int i = 1; i <= n; i++){if (!dfn[i])Tarjan(i, 0);}int ans = 0;for (int i = 2; i < cnt; i += 2) //邻接表存边从2开始{if (bridge[i])ans++;}cout << ans << '\n';for (int i = 2; i < cnt; i += 2){if (bridge[i])cout << to[i ^ 1] << "->" << to[i];}
}

三、求边双连通分量

在求出所有桥的基础上,把桥都删去,就可以得到边双连通分量

实现:先用Tarjan算法标记所有桥边,然后对整个无向图进行dfs,历遍过程不访问桥边,由此划分出每个连通块

//在求割边的基础上加上以下代码
int c[maxn], dcc;       //存储每个点的归属,和dcc的数量
void dfs(int x)
{c[x] = dcc;for (int i = head[x]; i; i = nex[i]){int y =to[i];if (c[y] || bridge[i])continue;dfs(y);}
}//下面加在main
for (int i = 1; i <= n; i++)
{if (!c[i]){dcc++;dfs(i);}
}
cout << dcc << '\n';
for (int i = 1; i <= n; i++)
{cout << i << " belongs to " << c[i] << '\n';
}

四、e-DCC缩点

在求出e-DCC的基础上,把每个e-DCC缩成一个点构成一棵新的树,存在邻接表中

int eto[maxn << 1], enex[maxn << 1], ehead[maxn], ecnt;
//在求出e-DCC的基础上加入以下代码
void add_dcc(int u, int v)
{eto[++ecnt] = v;enex[ecnt] = ehead[u];ehead[u] = ecnt;
}//下面代码加到main
ecnt = 1;
for (int i = 2; i <= cnt; i++)
{int x = to[i ^ 1], y = to[i];if (c[x] == c[y])continue;add_dcc(c[x], c[y]);
}
for (int i = 2; i < ecnt; i+=2)
{cout << eto[i ^ 1] << eto[i];    //输出边
}

五、求割点

如果x不是搜索树的根节点,则x是割点当且仅当搜索树上存在x的一个子节点y,满足:dfn(x)<=low(y)

int  dfn[maxn], low[maxn], point[maxn];
int head[maxn], to[maxn << 1], nex[maxn << 1];
int cnt, total, root;
void add(int u, int v)
{to[++cnt] = v;nex[cnt] = head[u];head[u] = cnt;
}
void Tarjan(int u)
{dfn[u] = low[u] = ++total;int child = 0;for (int i = head[u]; i; i = nex[i]){int v = to[i];if (!dfn[v]){Tarjan(v);low[u] = min(low[u], low[v]);if (low[v] >= dfn[u] && u != root)point[u] = 1;if (u == root && ++child >= 2)point[u] = 1;}elselow[u] = min(low[u], dfn[v]);}
}
void solve()
{int x, y, n, m;cin >> n >> m;for (int i = 1; i <= m; i++){cin >> x >> y;add(x, y);add(y, x);}for (int i = 1; i <= n; i++){if (!dfn[i]){root = i;Tarjan(i);}}int ans = 0;for (int i = 1; i <= n; i++){if (point[i])ans++;}cout << ans << '\n';for (int i = 1; i <= n; i++){if (point[i])cout << i << ' ';}
}

六、求点双连通分量

点双连通分量不是将割点删去后的剩余连通块,而是剩余连通块加上割点

我们将深搜时遇到的所有边加入到栈里面(第一次访问一个结点时入栈),当找到一个割点的时候(dfn(x)<=low(y)),就将这个割点往下走到的所有边弹出(从x开始向外弹出,直到y被弹出),而这些点构成的就是一个点双连通分量

int  dfn[maxn], low[maxn], point[maxn];
int head[maxn], to[maxn << 1], nex[maxn << 1];
int cnt, total, root;
stack<int>stk;
vector<int>dcc[maxn];
int vcnt;
void add(int u, int v)
{to[++cnt] = v;nex[cnt] = head[u];head[u] = cnt;
}
void Tarjan(int u)
{dfn[u] = low[u] = ++total;stk.push(u);if (u == root && head[u] == 0){dcc[++vcnt].push_back(u);return;}int child = 0;for (int i = head[u]; i; i = nex[i]){int v = to[i];if (!dfn[v]){Tarjan(v);low[u] = min(low[u], low[v]);if (low[v] >= dfn[u]){if (u != root)point[u] = 1;vcnt++;int temp;do{temp = stk.top();stk.pop();dcc[vcnt].push_back(temp);} while (temp != v);dcc[vcnt].push_back(u);}if (u == root && ++child >= 2)point[u] = 1;}elselow[u] = min(low[u], dfn[v]);}
}
void solve()
{int x, y, n, m;cin >> n >> m;for (int i = 1; i <= m; i++){cin >> x >> y;add(x, y);add(y, x);}for (int i = 1; i <= n; i++){if (!dfn[i]){root = i;Tarjan(i);}}cout << vcnt << '\n';for (int i = 1; i <= vcnt; i++){cout << dcc[i].size() << ' ';for (int j = 0; j < dcc[i].size(); j++){cout << dcc[i][j] << ' ';}cout << '\n';}
}

七、v-DCC缩点

由于一个割点可能属于多个点双连通分量,因此我们建一个包含p(割点数量)+t(v-DCC数量)个结点的新图,由每个割点连接v-DCC

//在求出v-DCC的基础上
int vto[maxn << 1], vnex[maxn << 1], vhead[maxn], vvcnt;
int newid[maxn],c[maxn];            //给割点的new id
void add_dcc(int u, int v)
{vto[++vvcnt] = v;vnex[vvcnt] = vhead[u];vhead[u] = vvcnt;
}//在main中加入以下代码
int num = vcnt;
for (int i = 1; i <= n; i++)
{if (point[i])newid[i] = ++num;
}
vvcnt = 1;
for (int i = 1; i <= vcnt; i++)
{for (int j = 0; j < dcc[i].size(); j++){int x = dcc[i][j];if (point[x]){add_dcc(i, newid[x]);add_dcc(newid[x], i);}elsec[x] = i;}
}
for (int i = 2; i < vvcnt; i += 2)
{cout << vto[i ^ 1] << ' ' << vto[i] << '\n';
}

图论(Tarjan算法与无向图)相关推荐

  1. 0x66.图论 - Tarjan算法与无向图连通性

    目录 一.无向图的割点与桥 割点 桥/割边 时间戳 搜索树 追溯值 二.割边判定法则 三.割点判定法则 1.luogu P3388 [模板]割点(割顶) 2.luogu P3469 [POI2008] ...

  2. C++算法篇:DFS超详细解析(2)--- tarjan算法求无向图割边

    <<<上一篇 系列文章目录 ①:无向图基本概念 ②:tarjan算法求无向图割边 前言 第一次写算法,讲得肯不透彻,有误还请指教awa 文章目录 系列文章目录 一.回顾 二.tarj ...

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

    tarjan算法求无向图的割点与桥 一篇tarjan算法求割点与桥的完整的解释,写的真的好认真 以下代码来自kuangbin的模板 4.5 图的割点.桥和双连通分支的基本概念 [点连通度与边连通度] ...

  4. tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)

    基本概念 给定无向连通图G = (V, E) 割点: 对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点 割边(桥) 若对于e∈E,从图中删去边e之 ...

  5. Tarjan算法求无向图割边割点、最近公共祖先的总结

     无向图tarjan求割边割点.最近公共祖先总结 割点:删除这个点之后整个图变成不连通的两个部分的点 割点集合:在一个无向图中删除该集合中的所有点,能使原图变成互不相连的连通块的点的集合 点连通度 ...

  6. 0x66 Tarjan算法与无向图联通性

    bzoj1123: [POI2008]BLO poj3694 先e-DCC缩点,此时图就变成了树,树上每一条边都是桥.对于添加边的操作,相当于和树上一条路径构环,导致该路径上所有边都不成为桥.那么找这 ...

  7. SPF Tarjan算法求无向图割点(关节点)入门题

    SPF 题目抽象,给出一个连通图的一些边,求关节点.以及每个关节点分出的连通分量的个数 邻接矩阵只要16ms,而邻接表却要32ms,  花费了大量的时间在加边上. //   time  16ms 1 ...

  8. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  9. POJ 2117 Electricity 割点 Tarjan算法

    [题意] 选择去掉无向图中的某个点,使得无向图连通分支数最大,输出这个最大数. 有三种情况: 1.无向图无边时,最大数=点数-1: 2.无向图有边并不存在割点时,最大数=原图连通分支数-1 3.无向图 ...

  10. c语言tarjan算法,无向图求割点和割边——Tarjan算法

    无向图中求割点集和割边集--Tarjan算法 割点和割边 定义 在一个无向图中,如果删除了某个顶点及与之相连的所有边,产生了一更大连通分量的子图,这样的顶点被称为割点或关节点.对于一个图的所有割点的集 ...

最新文章

  1. linux使用技巧:自动补全、常用快捷键* ? [] {}
  2. 代码jit_但这是不可能的,或者无法发现JIT破坏了您的代码。
  3. 四、Python第四课——Python中列表及其操作(增删改查)
  4. Linux服务器,服务管理--systemctl命令详解,设置开机自启动
  5. 重庆最狠的火锅,都是用来泡脚的
  6. read命令_dbatools Read-DbaBackupHeader命令的便捷功能
  7. 内置炊具行业调研报告 - 市场现状分析与发展前景预测
  8. tmadmin: command not found和tmadmin: error while loading shared libraries: libgpnet.so
  9. Nginx的configure各项中文说明
  10. 2022年华为杯中国研究生数学建模竞赛F题思路
  11. 微信小程序前端【订阅消息】遇到的问题及总结
  12. python如何编辑pdf_用Python实现一款永久免费的PDF编辑工具
  13. 车辆颜色识别opencv
  14. linux常用命令-文件搜索(locate_find_grep)
  15. jQuery 读书笔记之一
  16. 京东大数据,为什么这么牛?
  17. 软件测试时印象深刻的bug案例,请问你遇到过哪些印象深刻的bug,接口测试出现bug的原因有哪些?...
  18. java 容器排序_Java攻略第四章 容器类、排序
  19. 程序员的鄙视链-------哈哈哈,真逗
  20. java-php-python-汽车销售系统计算机毕业设计

热门文章

  1. 【转载】士兵突击 经典语录
  2. 【RemoteJoy】PSP图像采集方案
  3. 网页教程:为网站的Web Robot 设计路标
  4. 安卓Activity劫持与反劫持
  5. 数字图像来源:光学成像系统
  6. 安卓ttf格式的字体包_字体 | 新游黑体(游)精巧的日系字体~
  7. XX银行数据中心建设方案
  8. 调戏木马病毒的正确姿势——上
  9. android 修改imei源码,Android 修改imei、gps等信息分析
  10. 不同尺寸SMA头介绍以及之间的区别