日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

图上的文章(割点和桥)

發布時間:2025/5/22 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图上的文章(割点和桥) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題外話:
今天不想碼代碼了,知識普及的一天

注意:以下內容是在無向圖的基礎上

無向圖的割點

很久之前就知道這些名詞
今天終于可以來填坑了。。。

如果將連通圖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])。

int pre[N]; int back[N]; 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;if (!pre[v]) //v是子節點 {ch++;int lowv=dfs(v,now); //子節點能夠回溯到的最遠祖先 low=min(low,lowv);if (lowv>=pre[now]) //存在一個子節點與祖先不聯通,now就是割點 iscut[now]=1; //割點 }else if (pre[v]<pre[now]&&v!=fa) //是祖先,但不是直接父親 { //說明low是之前計算得到的所能返回的祖先節點 low=min(low,pre[v]);}}if (fa<0&&ch==1) iscut[now]=0; //性質一的驗證 back[now]=low;return low; }

注意

一定要寫v!=fa
因為邊(u,fa)不是反向邊,而是dfs樹邊(fa,u)的第二次遍歷
pre的初始化是0,第一次調用時,fa的初始值是-1

附:訓練指南(劉汝佳著)P312上有更詳盡的解釋

無向圖的橋

作為一種特殊情況,u為v的父節點,且low(v) < pre(u)
那么我們只要刪掉(u,v)這條邊就可以讓無向圖不連通了
滿足這個條件的邊叫做無向圖的橋
也就是說,我們知道了u是割點,而且(u,v)是橋、

int dfs(int now,int fa) {int ch=0;int low=pre[now]=++tt;for (int i=st[now];i;i=way[i].nxt){int v=way[i].y;if (!pre[v]){int lowv=dfs(v,now);low=min(low,lowv);ch++;if (lowv>pre[now]) //存在即合理 { //因為只找橋,所以在這里我就省去了割點的記錄an++;ans[an].x=min(now,v);ans[an].y=max(now,v); } }else if (pre[v]<pre[now]&&v!=fa){low=min(low,pre[v]);}}back[now]=low;return low; }

無向圖的雙連通分量

對于一個連通圖,如果任意兩點至少存在兩條“點不重復”的路徑,則說這個圖是點-雙連通的(一般簡稱雙連通)
這就相當于任意兩條邊都在同一個簡單環中,即內部無割點

類似的,如果任意兩個點至少存在“邊不重復”的路徑,我們說這個圖是邊-雙連通的。
即所有邊都不是橋

對于一張無向圖,點-雙連通的極大子圖稱為雙連通分量

邊-雙連通的極大子圖稱為邊雙連通分量(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

總結

以上是生活随笔為你收集整理的图上的文章(割点和桥)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。