無向圖的連通性與相關問題
一、相關概念:
給定一張無向圖\(G=(V,E)\):
1、割點:若\(x\in V\),從圖中刪去x以及與x相連的邊后,圖不連通,則x為割點。
2、割邊:若\(e\in 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;
// 將棧中的節點倒序輸出就是一條具體的歐拉回路方案
}
}