【笔记】【图论】强连通、割点和桥
(1)一些定义:
- 割点:若一个点删除后(与之相连的边统统去掉),无向图不再连通,那么此点称为割点。
- 桥:若一条边断去后,无向图不再连通,那么此边称为桥。桥有一个很好的性质,就是DFS一个无向图,那么这个过程必定要经过桥
- 1-连通图:具有割点的连通图。
- 2-连通图:至少要去掉两个顶点及其相关联的边,才能使该图的连通分支增加。
- 块:没有割点的无向图,称为2-连通分支,也称作块。
- 强连通:一个环,或者多个环相套。则一个强连通中的任何点都可以互相到达
(2)求强连通块——算法思路
随手一个简单强连通,
1->2->3->1
思考程序如何才能让计算机模拟人的眼睛,判断出这是一个强连通,
因为是边的相连导致强连通块,所以肯定要按照边的连通情况,遍历图,
这里显然dfs比bfs更合适,所以我们按照1->2->3的顺序遍历
接下来到了3->1的边,这个边肯定有特征,让计算机确定这是一个强连通
我们发现:
1)1号点已经遍历过
2)这是一条回边
这样我们可以设一个dfn的量,保存每个点遍历时的时间前后
现在我们看我们找强连通是为了什么:
1)计算有几个强连通
2)把环去掉,叫做tarjan缩点
那么对于目的1)
我们的计数肯定是记每个强连通的结束的点,
就是最先遍历的那个点,
那这个点的特征(和别的点的区别)在哪呢?
答案是他没有回边,就是他的子节点遍历中,遇不到一个点的dfn比他早
那么我们可以找一个记录的量,先叫做t,
这个t应该是记录每个点子节点中dfn的最小值
(因为最高点的dfn最小,其他的都比他的dfn大,那么我们要记录的就是dfn的下限)
那么我们就把t改成low,好听,不易重名
思路就出来了,找没有遍历过的点遍历,
在3节点时,找能到达的点集合,如果有一个遍历过的点,那他的dfn一定小,
我们就更新3节点
为了让2,这种中间点的dfn也发生改变,那么在2->3之后,也要更新2的dfn
所以我们的结构就出来了
但是那些遍历过的点,可能是别的强连通分量中的,所以一个stack,每次找完弹出一个强连通
ps:
tarjan中单个点也算一个强连通
#include<bits/stdc++.h> using namespace std; int n; const int N=1e5+10; vector <int > son[N]; int dfn[N],low[N]; int blocks,block_id;//blocks是连通块的数量 bool insta[N]; stack <int > sta; void tarjian(int x) { dfn[x]=low[x]=++tot; //进入先记录 sta.push(x),insta[x]=true; int sz = son[x].size(); for(int i=0;i<sz;i++) { int nx=son[x][i]; if(!dfn[nx]) { tarjian(nx); low[x]=min(low[x],low[nx]); } else if(insta[nx]) //如果这个点已经遍历过,且在sta中 => 是回边 => 会影响low { low[x]=min(low[x],low[nx]); } //比如G:12341 加上边4->5 5->6 4->6 //按照函数,操作4->6的时候,6访问过,但是不在栈中 //其实还是不清楚,如果没有判断insta,会出现什么情况 } if(low[x] == dfn[x] ) //说明x是此连通块的最高点,从栈中弹出所有 { //如果要统计点sta.top()归属哪个连通块 belong[x]=++block_id; while(sta.top()!=x) //弹出栈中所有点 { //如果要统计点sta.top()归属哪个连通块 belong[sta.top()]=block_id; insta[sta.top()]=false; sta.pop(); } insta[x]=false; sta.pop(); blocks++; } } int main() { for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i); return 0; }
对于目的2)
看题!
poj2186 Popular Cows
告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜。
缩点思路:将一个强连通分量->变成超级源点
这个超级源点,用fa表示,或者用新的sum=n,++sum也行
主要区别:
if(dfn[u]==low[u]) { color[u]=++sum; vis[u]=0; while(stack[top]!=u) { color[stack[top]]=sum; vis[stack[top--]]=0; } top--; }
注意,缩点之后的边要区分强连通内,和强连通外,
入度出度统计都要重新注意