无向图的连通性与相关问题
一、相关概念:
给定一张无向图(G=(V,E)):
1、割点:若(xin V),从图中删去x以及与x相连的边后,图不连通,则x为割点。
2、割边:若(ein E),从图中删去边e后,图不连通,则e为割边(桥)。
3、点(边)双联通分量:一张图不存在割点(边),则此图为点(边)双连通分量。
4、欧拉路、欧拉回路、欧拉图:若有一条从S到T的路径,满足恰好经过每一条边一次,则该路径为欧 拉路;特别地,当S=T时,此路为欧拉回路;一张所有点度数都为偶数的连通无向图为欧拉图。
二、Tarjan与割边、割点:
Tarjan能够在线性时间内求无向图的割点与割边。
对于Tarjan算法中定义的相关数组与变量不在赘述,直接讲一下判定法则。
割边判定法则:如果对于当前搜索点x,存在一条边(e=(x,y)),满足(low[y]>dfn[x]),则e为割边。
code:
void Tarjan(int x,int fx) {
RG int i,y;
dfn[x]=low[x]=++Time;
for(i=head[x];i;i=e[i].next)
if(!dfn[y=e[i].to]) {
Tarjan(y,x),low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) bri[i]=bri[i^1]=1;//成对储存,边从2开始
}
else if(y!=fx) low[x]=min(low[x],dfn[y]);
}
割边判定法则:如果对于当前搜索点x,满足(low[y]≥dfn[x]),则x是割点。
不过,需要注意的是,当x为Tarjan算法的起点时,x至少需要有两个上述节点才是割点。
void tarjan(int x) {
RG int i,k,y,num=0;
dfn[x]=low[x]=++Time;
for(i=head[x];i;i=e[i].next)
if(!dfn[y=e[i].to]) {
tarjan(y),low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y])
if(x!=RT||++num>1) cut[x]=1;
}
else low[x]=min(low[x],dfn[y]);
}
三、Tarjan与双连通分量
记点双连通分量为(v-DCC),边双连通分量为(e-DCC)。
(e-DCC) 求法:去掉图中的所有的割边,剩下的每一个连通块都是一个$e-DCC $(直接dfs即可)。
(e-DCC) 缩点:直接把每一个连通块对应一个节点即可,可以得到一棵树(或森林)。
void dfs(int x) {// 划分连通块
RG int i,y;
vis[x]=1,bel[x]=cnt;// 标记该点为所在e-DCC编号
for(i=head[x];i;i=e[i].next)
if(!vis[y=e[i].to]&&!bri[i]) dfs(y);
}
(v-DCC) 求法:不是直接去掉所有的割点,剩下的每一个连通块都是一个(v-DCC)!
因为一条割边只能在一个(e-DCC)中,但是一个割点去能在多个(v-DCC)中。
所以可以用栈维护一下经过的节点,当存在(low[y]≥dfn[x])成立时,无论x是否为根,都弹栈到y为止
所有这些弹出来的点与点x构成一个(v-DCC)。
(v-DCC) 缩点:每一个的(v-DCC)看成一个节点,同时每一个割点也单独看成一个节点。
每一个(v-DCC)向它包含的每一个割点连边,得到一颗树。事实上这棵树很棒!(下方的链接随笔e.e.)
割点的记录在新建的点上,其他点记录在对应的(v-DCC)上。
for(i=1;i<=n;++i)
if(cut[i]) bel[i]=++cnt,mtx[cnt]=1;
for(i=1,New();i<=cnt;++i)
for(j=0;j<vec[i].size();++j) // vec 存的是v-DCC的节点(含割点)
if(cut[x=vec[i][j]]) make(i,bel[x]);
else bel[x]=i;
利用缩点,我们就可以进一步解决有关无向图必经边、必经点的问题!←想了解?←。
四、欧拉路问题
无解判定:当且仅当每个点的度都为偶数才存在欧拉回路。
此处仅贴一下(dfs)求欧拉回路的模板(递归版):
void dfs(int x) {
RG int i,y;
for(i=head[x];i;i=e[i].next) {
y=e[i].id;
if(!vis[y]) vis[y]=1,dfs(e[i].to),sta[++top]=y;
// 将栈中的节点倒序输出就是一条具体的欧拉回路方案
}
}