hiho图的联通性(自留)
無向圖割邊割點(diǎn)算法
而當(dāng)(u,v)為樹邊且low[v]>dfn[u]時(shí),表示v節(jié)點(diǎn)只能通過該邊(u,v)與u連通,那么(u,v)即為割邊。
1 void dfs(int u) { 2 //記錄dfs遍歷次序 3 static int counter = 0; 4 5 //記錄節(jié)點(diǎn)u的子樹數(shù) 6 int children = 0; 7 8 ArcNode *p = graph[u].firstArc; 9 visit[u] = 1; 10 11 //初始化dfn與low 12 dfn[u] = low[u] = ++counter; 13 14 for(; p != NULL; p = p->next) { 15 int v = p->adjvex; 16 17 //節(jié)點(diǎn)v未被訪問,則(u,v)為樹邊 18 if(!visit[v]) { 19 children++; 20 parent[v] = u; 21 dfs(v); 22 23 low[u] = min(low[u], low[v]); 24 25 //case (1) 26 if(parent[u] == NIL && children > 1) { 27 printf("articulation point: %d\n", u); 28 } 29 30 //case (2) 31 if(parent[u] != NIL && low[v] >= dfn[u]) { 32 printf("articulation point: %d\n", u); 33 } 34 35 //bridge 36 if(low[v] > dfn[u]) { 37 printf("bridge: %d %d\n", u, v); 38 } 39 } 40 41 //節(jié)點(diǎn)v已訪問,則(u,v)為回邊 42 else if(v != parent[u]) { 43 low[u] = min(low[u], dfn[v]); 44 } 45 } 46 } View Code?
邊雙聯(lián)通分量算法
對(duì)于一個(gè)無向圖的子圖,當(dāng)刪除其中任意一個(gè)點(diǎn)后,不改變圖內(nèi)點(diǎn)的連通性,這樣的子圖叫做點(diǎn)的雙連通子圖。而當(dāng)子圖的邊數(shù)達(dá)到最大時(shí),叫做點(diǎn)的雙連通分量。
直觀的做法自然先用上周的算法求出所有橋,去掉所有橋之后再做DFS求出每一個(gè)連通子圖。我們這周要介紹一種更"抽象"的算法,通過在Tarjan算法當(dāng)中巧妙地用一個(gè)棧來統(tǒng)計(jì)出每一個(gè)組內(nèi)的節(jié)點(diǎn),其代碼如下:
1 void dfs(int u) { 2 //記錄dfs遍歷次序 3 static int counter = 0; 4 5 //記錄節(jié)點(diǎn)u的子樹數(shù) 6 int children = 0; 7 8 ArcNode *p = graph[u].firstArc; 9 visit[u] = 1; 10 11 //初始化dfn與low 12 dfn[u] = low[u] = ++counter; 13 14 //將u加入棧 15 stack[++top] = u; 16 17 for(; p != NULL; p = p->next) { 18 int v = p->adjvex; 19 20 //節(jié)點(diǎn)v未被訪問,則(u,v)為樹邊 21 if(!visit[v]) { 22 children++; 23 parent[v] = u; 24 dfs(v); 25 26 low[u] = min(low[u], low[v]); 27 if (low[v] > dfn[u]) { 28 printf("bridge: %d %d\n", u, v); // 該邊是橋 29 bridgeCnt++; 30 } 31 } 32 33 //節(jié)點(diǎn)v已訪問,則(u,v)為回邊 34 else if(v != parent[u]) { 35 low[u] = min(low[u], dfn[v]); 36 } 37 } 38 39 if (low[u] == dfn[u]) 40 { 41 // 因?yàn)閘ow[u] == dfn[u],對(duì)(parent[u],u)來說有dfn[u] > dfn[ parent[u] ],因此low[u] > dfn[ parent[u] ] 42 // 所以(parent[u],u)一定是一個(gè)橋,那么此時(shí)棧內(nèi)在u之前入棧的點(diǎn)和u被該橋分割開 43 // 則u和之后入棧的節(jié)點(diǎn)屬于同一個(gè)組 44 將從u到棧頂所有的元素標(biāo)記為一個(gè)組,并彈出這些元素。 45 } 46 } View Code?
強(qiáng)連通分量
對(duì)于有向圖上的2個(gè)點(diǎn)a,b,若存在一條從a到b的路徑,也存在一條從b到a的路徑,那么稱a,b是強(qiáng)連通的。 對(duì)于有向圖上的一個(gè)子圖,若子圖內(nèi)任意點(diǎn)對(duì)(a,b)都滿足強(qiáng)連通,則稱該子圖為強(qiáng)連通子圖。 非強(qiáng)連通圖有向圖的極大強(qiáng)連通子圖,稱為強(qiáng)連通分量。 特別地,和任何一個(gè)點(diǎn)都不強(qiáng)連通的單個(gè)點(diǎn)也是一個(gè)強(qiáng)連通分量。1 tarjan(u) 2 { 3 Dfn[u]=Low[u]=++Index // 為節(jié)點(diǎn)u設(shè)定次序編號(hào)和Low初值 4 Stack.push(u) // 將節(jié)點(diǎn)u壓入棧中 5 for each (u, v) in E // 枚舉每一條邊 6 if (v is not visted) // 如果節(jié)點(diǎn)v未被訪問過 7 tarjan(v) // 繼續(xù)向下找 8 Low[u] = min(Low[u], Low[v]) 9 else if (v in Stack) // 如果節(jié)點(diǎn)v還在棧內(nèi)(很重要,無向圖沒有這一步) 10 Low[u] = min(Low[u], Dfn[v]) 11 if (Dfn[u] == Low[u]) // 如果節(jié)點(diǎn)u是強(qiáng)連通分量的根 12 repeat 13 v = Stack.pop // 將v退棧,為該強(qiáng)連通分量中一個(gè)頂點(diǎn) 14 mark v // 標(biāo)記v,同樣通過棧來找連通分量 15 until (u == v) 16 } View Code
scc + 縮點(diǎn) + topo
?
點(diǎn)的雙連通分量
對(duì)于一個(gè)無向圖的子圖,當(dāng)刪除其中任意一個(gè)點(diǎn)后,不改變圖內(nèi)點(diǎn)的連通性,這樣的子圖叫做點(diǎn)的雙連通子圖。而當(dāng)子圖的邊數(shù)達(dá)到最大時(shí),叫做點(diǎn)的雙連通分量。
???????????????????????????????????????
對(duì)于橋的兩種情況,它分割個(gè)區(qū)域數(shù)剛好就等于割點(diǎn)數(shù)+1;而連通分量內(nèi)的割點(diǎn)同樣也是,每存在一個(gè)割點(diǎn),點(diǎn)的雙連通分量就增加一個(gè)。
點(diǎn)的雙連通分量就等于割點(diǎn)數(shù)量加1。
每存在一個(gè)割點(diǎn),就把一個(gè)區(qū)域一分為二,所以最后的結(jié)果也就是統(tǒng)計(jì)割點(diǎn)的數(shù)量就可以了。而對(duì)于分組具體情況,我們?nèi)匀徊捎脳磔o助我們記錄
1 void dfs(int u) { 2 //記錄dfs遍歷次序 3 static int counter = 0; 4 5 //記錄節(jié)點(diǎn)u的子樹數(shù) 6 int children = 0; 7 8 ArcNode *p = graph[u].firstArc; 9 visit[u] = 1; 10 11 //初始化dfn與low 12 dfn[u] = low[u] = ++counter; 13 14 for(; p != NULL; p = p->next) { 15 int v = p->adjvex; 16 if(edge(u,v)已經(jīng)被標(biāo)記) continue; 17 18 //節(jié)點(diǎn)v未被訪問,則(u,v)為樹邊 19 if(!visit[v]) { 20 children++; 21 parent[v] = u; 22 edgeStack[top++] = edge(u,v); // 將邊入棧 23 dfs(v); 24 25 low[u] = min(low[u], low[v]); 26 27 //case (1) 28 if(parent[u] == NIL && children > 1) { 29 printf("articulation point: %d\n", u); 30 // mark edge 31 // 將邊出棧,直到當(dāng)前邊出棧為止,這些邊標(biāo)記為同一個(gè)組 32 do { 33 nowEdge = edgeStack[top]; 34 top--; 35 // 標(biāo)記nowEdge 36 } while (nowEdge != edge(u,v)) 37 } 38 39 //case (2) 40 if(parent[u] != NIL && low[v] >= dfn[u]) { 41 printf("articulation point: %d\n", u); 42 // mark edge 43 // 將邊出棧,直到當(dāng)前邊出棧為止,這些邊標(biāo)記為同一個(gè)組 44 do { 45 nowEdge = edgeStack[top]; 46 top--; 47 // 標(biāo)記nowEdge 48 } while (nowEdge != edge(u,v)) 49 } 50 51 } 52 53 //節(jié)點(diǎn)v已訪問,則(u,v)為回邊 54 else if(v != parent[u]) { 55 edgeStack[top++] = edge(u,v); 56 low[u] = min(low[u], dfn[v]); 57 } 58 } 59 } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/usedrosee/p/4693121.html
總結(jié)
以上是生活随笔為你收集整理的hiho图的联通性(自留)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Uediter的引用和取值
- 下一篇: spring集合的注入