第1节 连通性强连通、割点和桥(一)
文章目錄
- 無向圖割點、橋、雙連通分量
- Tarjan算法求割點和橋(割邊)
- 代碼:
- 邊雙連通分量 和 點雙連通分量
- 代碼
- 邊雙連通分量 和 點雙連通分量 的縮點
- 有向圖的弱連通與強連通
- 強連通分量
- Kosaraju算法
- Tarjan算法
- 代碼:
無向圖割點、橋、雙連通分量
? 給定無向聯(lián)通圖G=(V,E) ? 對于一個點x,若從圖中刪除x及所有與x相連的邊,圖不再聯(lián)通,x是G的割點
? 對于一條邊e,從圖中刪去e,圖不再聯(lián)通,e的x的割邊
? 一個圖如果不存在割點,則它是一個點雙連通圖,一個圖的極大點雙連通子圖是他的點雙連
通分量。
? 一個圖如果不存在割邊,則它是一個邊雙連通圖,一個圖的極大邊雙連通子圖是他的邊雙連
通分量。
Tarjan算法求割點和橋(割邊)
時間戳dfn :第n個搜到這個點
返祖邊:搜索樹上一個點連向其祖先節(jié)點的邊
橫插邊:搜索樹上一個點連向它另一條支鏈上的點的邊----在無向圖中不存在
追溯值low:當前點及其子樹的返祖邊能連到的dfn值最小的點
如果<u,v>是搜索樹的邊:low[u]=min(low[u],low[v])
? u點是割點,v是u搜索樹上的一個兒子:dfn[u] <= low[v] ——v的子樹中沒有返祖邊能跨越u點;有多個兒子的根節(jié)點
比如圖中的點10和點7
? 邊是橋,搜索樹上v是u的兒子:dfn[u]<low[v]——v的子樹中沒有返祖邊能跨越<u,v>這條邊
代碼:
void tarjan(int x)//求割點 {dfn[x]=low[x]=++cnt;int flag=0;for(int i=head[x];i;i=edge[i].next){int v=edge[i].v;if(!dfn[v]){tarjan(v);low[x]=min(low[x],low[v]);if(low[v]>=dfn[x]){flag++;if(x!=root||flag>1)book[x]=1;//flag>!說明根有兩個以上的兒子 }}else low[x]=min(low[x],dfn[v]);} }求割邊:if(low[v] > dfn[x])
邊雙連通分量 和 點雙連通分量
? 把橋刪了每個連通塊都是一個邊雙聯(lián)通分量——標記出橋之后dfs一遍即可
? 點雙連通分量要復雜一些——一個割點可能屬于多個雙聯(lián)通分量
點雙連通分量性質(zhì):
任意兩點間至少存在兩條點不重復的路徑等價于圖中刪去任意一個點都不會改變圖的連通性,即BCC中無割點
若BCC間有公共點,則公共點為原圖的割點
無向連通圖中割點一定屬于至少兩個BCC,非割點只屬于一個BC
點雙連通分量求法::
? 維護一個棧
? 第一次訪問某個節(jié)點時,將其入棧
? 當割點判斷法則中dfn[x]<=low[y]成立時,不斷從棧中彈出節(jié)點,直到y(tǒng)被彈出,這些被彈出的點和x一起構(gòu)成一個點雙連通分量
代碼
#include<cstdio> #include<cctype> #include<vector> using namespace std; struct edge {int to,pre; }edges[1000001]; int head[1000001],dfn[1000001],dfs_clock,tot; int num;//BCC數(shù)量 int stack[1000001],top;//棧 vector<int>bcc[1000001]; int tarjan(int u,int fa) {int lowu=dfn[u]=++dfs_clock;for(int i=head[u];i;i=edges[i].pre)if(!dfn[edges[i].to]){stack[++top]=edges[i].to;//搜索到的點入棧 int lowv=tarjan(edges[i].to,u);lowu=min(lowu,lowv);if(lowv>=dfn[u])//是割點或根 {num++;while(stack[top]!=edges[i].to)//將點出棧直到目標點 bcc[num].push_back(stack[top--]);bcc[num].push_back(stack[top--]);//目標點出棧 bcc[num].push_back(u);//不要忘了將當前點存入bcc }}else if(edges[i].to!=fa)lowu=min(lowu,dfn[edges[i].to]);return lowu; } void add(int x,int y)//鄰接表存邊 {edges[++tot].to=y;edges[tot].pre=head[x];head[x]=tot; } int main() {int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);add(x,y),add(y,x);}for(int i=1;i<=n;i++)//遍歷n個點tarjan if(!dfn[i]){stack[top=1]=i;tarjan(i,i);}for(int i=1;i<=num;i++){printf("BCC#%d: ",i);for(int j=0;j<bcc[i].size();j++)printf("%d ",bcc[i][j]);printf("\n");}return 0; }邊雙連通分量 和 點雙連通分量 的縮點
? 每個邊雙連通分量縮成一個點,再用原來的橋把他們連起來
? 點雙聯(lián)通分量因為一個割點可能包含在多個點雙連通分量里面,所以我們將每個割點保留割點與其所在的點雙連通分量連邊即可。
有向圖的弱連通與強連通
? 在有向圖G=(V,E)中,如果對于任意兩點u,v,存在一條從u到v或者從v到u的路徑——弱聯(lián)通
? 在有向圖G=(V,E)中,如果對于任意兩點u,v都互相可達——強聯(lián)通
強連通分量
? 有向圖強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路
徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly connected)。 ? 如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。
? 有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。
Kosaraju算法
? 對原圖進行一次dfs(任意起點)
? 在第一次形成的森林的每一顆樹里,以第一次搜索出棧時間的逆序?qū)Ψ磮D進行dfs,這次搜索A能到達的點和A都在一個強連通分量里面
Tarjan算法
? Dfn[n]時間戳
? Low[n]他和他的子樹里返祖邊和橫叉邊能連到還沒出棧的dfn最小的點
? 在dfs的時候維護一個棧第一次訪問到某個點就將其加入到棧中,當一個點x的dfn[x] == low[x] 時,他就是這個強連通分量里面在搜索樹中最高的點,將棧里點出棧直到x也出棧為止,這些點組成一個強連通分量。
代碼:
void tarjan(int x) {dfn[x]=low[x]=++cnt;stack[++top]=x;vis[x]=1;//x是否在棧里 for(int i=head[x];i;i=edge[i].next){int v=edge[i].v;if(!dfn[v]){tarjan(v);low[x]=min(low[x],low[y]);}else if(vis[v])low[x]=min(low[x],dfn[v]);if(dfn[x]==low[x])//是否是強連通分量最高的點 {ans++;//新強連通的標號 do{int cur=stack[top--];//棧頂?shù)狞c vis[cur]=false;num[cur]=ans;}while(x!=cur);}}} 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的第1节 连通性强连通、割点和桥(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: aes key长度_AES加密(1):
- 下一篇: 第1节 连通性强连通、割点和桥 例题