图上的文章(割点和桥)
題外話:
今天不想碼代碼了,知識普及的一天
注意:以下內容是在無向圖的基礎上
無向圖的割點
很久之前就知道這些名詞
今天終于可以來填坑了。。。
如果將連通圖G中的某個點及和這個點相關的邊刪除后,將使連通分量數量增加,那么這個點就稱為圖G的割點或是接合點。
如果一個無向圖沒有割點,則這樣的圖被稱為雙連通圖。
關于圖的割點,有如下兩條性質:
【性質一】
如果深度優先搜索樹的根節點至少有兩個以上的子節點,則根節點是割點。顯然去掉根節點后將得到以子節點為根結點的森林。
【性質二】
在深度優先搜索樹中,v存在一個子節點不能通過后向邊到達v的祖先節點,則節點v是割點。
也就是說從v的子節點開始沒有一條邊能夠回到v的祖先節點,那么當去掉v時將會使得v的子孫節點與v的祖先節點之間失去聯系。必定會使得圖不再連通。
可以通過對圖的深度優先搜索,
加上上面的兩條性質來尋找圖中的割點。
在對圖進行深度優先搜索時,記錄下遍歷的順序pre_order,
則pre_order從小到大就代表了從根節點一直到葉子節點。
在遍歷的時候同時得出每一個節點通過自己或是子孫節點的后向邊(不是從父親直接來的邊)所能達到的最原始的祖先,
也就是pre_order最小的節點,記錄在back_order中。
那么如果一個節點的back_order>=父節點的pre_order,
說明該節點的子孫節點不存在一條后向邊到達父節點的祖先節點,則根據第二條性質,該節點必是割點。
back_order的計算
由于back_order記錄的是節點所能返回到的最原始的祖先節點,
初始化back_order[v]=pre_order[v],
及頂點v所能返回的最原始祖先就是自己。
在對v進行深搜時,如果與v相鄰的下一個節點w沒被訪問過,說明(v,w)是生成樹上的邊,
那么back_order[v]就應該是v和w中能返回到最原始祖先的點的back_order,
即back_order[v]=min(back_order[v],back_order[w])
如果w已經被訪問過,說明w就是v的祖先,那么back_order[v]就應該是v之前計算得到的所能返回的祖先節點與w之間最小的一個,
即back_order[v]=min(back-order[v],pre_order[w])。
注意
一定要寫v!=fa
因為邊(u,fa)不是反向邊,而是dfs樹邊(fa,u)的第二次遍歷
pre的初始化是0,第一次調用時,fa的初始值是-1
附:訓練指南(劉汝佳著)P312上有更詳盡的解釋
無向圖的橋
作為一種特殊情況,u為v的父節點,且low(v) < pre(u)
那么我們只要刪掉(u,v)這條邊就可以讓無向圖不連通了
滿足這個條件的邊叫做無向圖的橋
也就是說,我們知道了u是割點,而且(u,v)是橋、
無向圖的雙連通分量
對于一個連通圖,如果任意兩點至少存在兩條“點不重復”的路徑,則說這個圖是點-雙連通的(一般簡稱雙連通)
這就相當于任意兩條邊都在同一個簡單環中,即內部無割點
類似的,如果任意兩個點至少存在“邊不重復”的路徑,我們說這個圖是邊-雙連通的。
即所有邊都不是橋
對于一張無向圖,點-雙連通的極大子圖稱為雙連通分量
邊-雙連通的極大子圖稱為邊雙連通分量(BCC),
也就是說,在刪除所有的橋之后,每個連通分量對應原圖中的一個邊-雙連通分量
下面給出點雙(BCC)的計算方法
int pre[N],iscut[N],bccno[N],tot=0,bcc_cut; vector bcc[N]; stack<node> S;int dfs(int now,int fa) {int low=pre[now]=++tot;int ch=0;for (int i=st[now];i;i=way[i].nxt){int v=way[i].y;node e=way[i];if (!pre[v]){S.push(e);ch++;int lowv=dfs(v,now);low=min(low,lowv);if (lowv>=pre[now]){iscut[now]=1;bcc_cnt++;bcc[bcc_cnt].clear;for (;;){node x=S.top();S.pop();if (bccno[x.x]!=bcc_cnt){bcc[bcc_cnt].push_back(x.x);bccno[x.x]=bcc_cnt;}if (bccno[x.y]!=bcc_cnt){bcc[bcc_cnt].push_back(x.y);bccno[x.y]=bcc_cnt;}if (x.x==now&&x.y==v) break;}}else if (pre[v]<pre[now]&&v!=fa){S.push(e);low=min(low,pre[v]);}}}if (fa<0&&ch==1) iscut[now]=0;return low; }void find_bcc(int n) {memset(pre,0,sizeof(pre));memset(iscut,0,sizeof(iscut));memset(bccno,0,sizeof(bccno));tot=bcc_cnt=0;for (int i=1;i<=n;i++) //森林 if (!pre[i])dfs(i,-1); }邊雙的計算方法更簡單,分兩個步驟,
先作一次dfs標記出所有的橋,然后再做一次dfs找出邊雙
因為邊雙不能經過橋而且兩個邊雙之間沒有公共點,
所以在dfs的時候只要保證不經過橋即可
轉載于:https://www.cnblogs.com/wutongtong3117/p/7673121.html
總結
以上是生活随笔為你收集整理的图上的文章(割点和桥)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ab的压力测试(转)
- 下一篇: 最近在弄ionic3的时候遇到的一些问题