点双连通和边双连通
- 连通的概念:在无向图中,所有点能互相到达
- 连通分量:互相联通的子图
- 点双连通:删掉一个点之后,图仍联通
- 边双连通:删掉一条边之后,图仍联通
tarjan 算法:
该算法是R.Tarjan发明的。对图深度优先搜索, dfn[i]为第i个结点在搜索树中的深度,low[i]为第i个结点的子树的所有儿子连接到的最上面的结点层数。根据定义,则有:
Low(u)=Min
{
dfn(u),
dfn(v) ,(u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点
Low(v) ,(u,v)为树枝边(父子边)
}
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。
一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)
求双连通分量
- 对于点双连通分量,实际上在求割点的过程中就能顺便把每个点双连通分量求出。建立一个栈,存储当前双连通分量,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。割点可以属于多个点双连通分量,其余点和每条边只属于且属于一个点双连通分量支。
(这种还没有实现过,不过我认为显然如果把割点标记出来,跑dfs也能求出点双连通分支,虽然代码量会上升,不过还挺好打的,下面例题二类似) - 对于边双连通分量,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分量。桥不属于任何一个边双连通分量,其余的边和每个顶点都属于且只属于一个边双连通分量。
构造双连通图
- 一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1
- 统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
模板
int root, cnt;
int vis[maxn], dfn[maxn], low[maxn];
bool cut[maxn];
//vector<pair<int, int>>bridge;
void dfs(int u, int fa)
{
int son=0;
vis[u]=1;
dfn[u]=low[u]=++cnt;
for (int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if (v==fa) continue;
if (vis[v]==1) low[u]=min(low[u], dfn[v]); //返祖边
if (vis[v]==0)
{
dfs(v, u);
son++;
low[u]=min(low[u], low[v]);
if ( (u==root && son>1) || (u!=root && low[v]>=dfn[u]))
{
cut[u]=true;
//if(low[v] > dfn[u]) bridge.push_back({u, v}); //(u, v) 是桥
}
}
}
vis[u]=2;
}
void tarjan_init()
{
memset(vis, 0, sizeof(vis));
memset(cut, 0, sizeof(cut));
cnt=0; root=1;
//bridge.clear();
}
例题
POJ1144【基础】
题目大意:
给出一个无向图,求出有多少个割点。
输入:
有若干组测试数据。每一组测试数据的第一行有一个整数 n,表示有 n
(1<=n<100)个点,n=0 时测试数据结束。接下来有若干行,每一行第一个整
数 u 表示这一行描述的是以 u 为起点的边,接下来有若干个整数 vi 表示有一条
边 u-vi,u=0 时表示这一组测试数据结束。
输出:
对于每一组测试数据,输出一个整数,即有多少个割点。
模板题,没太多要说的
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstdlib>
#include<cstring>
using