求無向圖的割點和橋模板(tarjan)

一.基本概念html

    1.橋:若無向連通圖的邊割集中只有一條邊,則稱這條邊爲割邊或者橋 (離散書上給出的定義。。node

    通俗的來講就是無向連通圖中的某條邊,刪除後獲得的新圖聯通分支至少爲2(即不連通;ios

    2.割點:若無向連通圖的點割集中只有一個點,則稱這個點爲割點或者關節點 ;算法

    通俗的來講就是無向連通圖中的某條邊,刪除後獲得的新圖連通分支至少爲2;ide

 

二:tarjan算法求割點和橋spa

    1.割點:1)當前節點爲樹根的時候,條件是「要有多餘一棵子樹」;code

         若是這有一顆子樹,去掉這個點也沒有影響,若是有兩顆子樹,去掉這點,兩顆子樹就不連通了;htm

                  2)當前節點u不是樹根的時候,條件是「low[v]>=dfn[u]」,也就是在u以後遍歷的點,可以向上翻,blog

      最多到u,若是能翻到u的上方,那就有環了,去掉u以後,圖仍然連通。ip

    2.橋:若一條無向邊(u,v)是橋,

                  1)當且僅當無向邊(u,v)是樹枝邊,須要知足dfn(u)<low(v),即v向上翻不到u及其以上的點,

      那麼u--v之間必定可以有1條或者多條邊不能刪去, 由於他們之間有部分無環,是橋,

      若是v能上翻到u那麼u--v就是一個環,刪除其中一條路徑後,仍然是連通的。

    3.注意點:1)求橋的時候:由於邊是無方向的,因此父親孩子節點的關係須要本身規定一下,

                     在tarjan的過程當中if(v不是u的父節點) low[u]=min(low[u],dfn[v]);

                     由於若是v是u的父親,那麼這條無向邊就被誤認爲是環了。

                     2)找橋的時候:注意看看有沒有重邊,有重邊的邊必定不是橋,也要避免誤判。

      4.也能夠先進行tarjan(),求出每個點的dfn和low,並記錄dfs過程當中的每一個點的父節點,

    遍歷全部點的low,dfn來尋找橋和割點

 

代碼:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <vector>
 4 #include <string.h>
 5 using namespace std;
 6 
 7 const int MAXN=1e5+10;
 8 vector<int> mp[MAXN];
 9 bool is_cut[MAXN];
10 int n, m, count=0;
11 int low[MAXN], dfn[MAXN], pre[MAXN];//pre[u]記錄u的父親節點編號
12 //dfn[u]記錄節點u在DFS過程當中被遍歷到的次序號,low[u]記錄節點u或u的子樹經過非父子邊追溯到最先的祖先節點(即DFS次序號最小
13 
14 void tarjan(int u, int fu){
15     pre[u]=fu;//記錄當前u的父親節點
16     dfn[u]=low[u]=count++;
17     for(int i=0; i<mp[u].size(); i++){
18         int v=mp[u][i];
19         if(dfn[v]==-1){
20             tarjan(v, u);
21             low[u]=min(low[u], low[v]);
22         }else if(fu!=v){//若是v是u的父親的話,即有重邊,那麼不多是橋
23             low[u]=min(low[u], dfn[v]);
24         }
25     }
26 }
27 
28 void solve(void){
29     int rootson=0;
30     tarjan(1, 0);
31     for(int i=2; i<=n; i++){
32         int v=pre[i];
33         if(v==1){
34             rootson++;//統計根節點的子樹個數,若其不小於2,即爲割點
35         }else if(low[i]>=dfn[v]){
36             is_cut[v]=true;
37         }
38     }
39     if(rootson>1) is_cut[1]=true;
40     puts("割點爲:");
41     for(int i=1; i<=n; i++){//輸出割點
42         if(is_cut[i]){
43             printf("%d ", i);
44         }
45     }
46     puts("\n橋爲:");
47     for(int i=1; i<=n; i++){
48         int v=pre[i];
49         if(v>0&&low[i]>dfn[v]){
50             printf("%d %d\n", v, i);
51         }
52     }
53     puts("");
54 }
55 
56 int main(void){
57     scanf("%d%d", &n, &m);
58     for(int i=0; i<m; i++){
59         int x, y;
60         scanf("%d%d", &x, &y);
61         mp[x].push_back(y);
62         mp[y].push_back(x);
63     }
64     memset(dfn, -1, sizeof(dfn));
65     memset(low, -1, sizeof(low));
66     solve();
67     return 0;
68 }
View Code

 

求橋的另外一種寫法(更快一點):

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 using namespace std;
 5 
 6 const int MAXN = 1e5 + 10;
 7 
 8 struct node{
 9     int v, next, use;
10 }edge[MAXN << 2];
11 
12 bool bridge[MAXN];
13 int low[MAXN], dfn[MAXN], vis[MAXN];
14 int head[MAXN], pre[MAXN], ip, sol, count;
15 
16 void init(void){
17     memset(head, -1, sizeof(head));
18     memset(vis, false, sizeof(vis));
19     memset(bridge, false, sizeof(bridge));
20     count = sol = ip = 0;
21 }
22 
23 void addedge(int u, int v){
24     edge[ip].v = v;
25     edge[ip].use = 0;
26     edge[ip].next = head[u];
27     head[u] = ip++;
28 }
29 
30 void tarjan(int u){
31     vis[u] = 1;
32     dfn[u] = low[u] = count++;
33     for(int i = head[u]; i != -1; i = edge[i].next){
34         if(!edge[i].use){
35             edge[i].use = edge[i ^ 1].use = 1;
36             int v = edge[i].v;
37             if(!vis[v]){
38                 pre[v] = u;
39                 tarjan(v);
40                 low[u] = min(low[u], low[v]);
41                 if(dfn[u] < low[v]){
42                     sol++;
43                     bridge[v] = true;
44                 }
45             }else if(vis[v] == 1){
46                 low[u] = min(low[u], dfn[v]);
47             }
48         }
49     }
50     vis[u] = 2;
51 }
52 
53 int main(void){
54     int n, m, q, x, y, cas = 1;
55     while(~scanf("%d%d", &n, &m)){
56         if(!n && !m) break;
57         init();
58         for(int i = 0; i < m; i++){
59             scanf("%d%d", &x, &y);
60             addedge(x, y);
61             addedge(y, x);
62         }
63         pre[1] = 1;
64         tarjan(1);
65         for(int i = 1; i <= n; i++){
66             if(bridge[i]) cout << i << " " << pre[i] << endl;
67         }
68     }
69     return 0;
70 }
View Code

 

以上參考博客:http://www.cnblogs.com/c1299401227/p/5402747.html

相關文章
相關標籤/搜索