tarjan算法 求桥

边双联通分量:在一个无向图中,存在一个极大子图,删除任意一条边之后仍然是一个无向图。

桥:在无向图中,存在某条边,删除该边之后,该无向图将会被分割成两个无向图。

  1 #include <iostream>
  2 #include <vector>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <queue>
  6 #include <map>
  7 #include <algorithm>
  8 
  9 using namespace std;
 10 
 11 #define ll long long
 12 #define pb push_back
 13 #define fi first
 14 #define se second
 15 
 16 const int N = 1e5 + 10;
 17 int dfn[N]; //时间戳
 18 int low[N]; //能到达最早点
 19 int s[N]; //栈
 20 int scc_no[N];//属于哪个scc
 21 int scc_cnt[N]; //该scc有几个点
 22 vector<int > scc[N];//缩点
 23 vector<int > E[N]; //边
 24 vector<int > new_E[N];//缩点后的图
 25 int boss[N];
 26 int SCC; //第几个scc
 27 int tim;//dfs序号
 28 int top;//栈顶
 29 int cut_cnt; //割桥数
 30 int n, m; //点数  边数
 31 struct _cut{
 32     int x, y;
 33 };
 34 vector<_cut > cut;//桥
 35 
 36 //时间复杂度  O(m)
 37 //无向图中求的是边双联通分量,ins[]数组没作用
 38 
 39 void init(){
 40     for(int i = 0; i <= n; ++i){
 41         dfn[i] = low[i] = 0;
 42         scc_cnt[i] = 0;
 43         scc_no[i] = 0;
 44         scc[i].clear();
 45         E[i].clear();
 46     }
 47     SCC = tim = top = cut_cnt = 0;
 48     cut.clear();
 49 }
 50 
 51 void tarjan(int now, int pre){
 52     dfn[now] = low[now] = ++tim;
 53     s[top++] = now;
 54 
 55     int pre_cnt = 0;
 56     for(auto to : E[now]){
 57         //处理一条无向边
 58         if(to == pre && pre_cnt == 0) { pre_cnt = 1; continue; }
 59 
 60         if(!dfn[to]){
 61             tarjan(to, now);
 62             low[now] = min(low[now], low[to]);
 63 
 64             //to不能到达 now或者now之前的点
 65             if(dfn[now] < low[to]){
 66                 cut.pb(_cut{now, to});
 67             }
 68 
 69         }else low[now] = min(low[now], dfn[to]);
 70     }
 71 
 72     if(dfn[now] == low[now]){
 73         int sum = 0; //该scc的点数
 74         ++SCC;//第几个scc
 75         boss[SCC] = now;
 76         int tmp;//暂存点
 77 
 78         do{
 79             tmp = s[--top];//取点
 80             ++sum;//点数++
 81             scc_no[tmp] = SCC;//属于哪个scc
 82             //scc[SCC].pb(tmp);//放入这个连通图,缩点
 83         }while(tmp != now);
 84         //scc_cnt[SCC] = sum;//该scc的点数
 85     }
 86 }
 87 //桥信息
 88 void show_info(){
 89     printf("cut bridge = (%d)\n", cut_cnt);
 90     for(auto c : cut){
 91         printf("(%d - %d)\n", c.x, c.y);
 92     }
 93 }
 94 
 95 void solve(){
 96 
 97     //int _case = 0;
 98     while(~scanf("%d%d", &n, &m) && (n + m)){
 99         //scanf("%d%d", &n, &m);
100         //初始化每组数据
101         init();
102         
103         //读边
104         for(int i = 1; i <= m; ++i){
105             int u, v;
106             scanf("%d%d", &u, &v);
107             E[u].pb(v);
108             E[v].pb(u);
109         }
110 
111         //可能不是一个连通图
112         for(int now = 1; now <= n; ++now){
113             if(!dfn[now]){
114                 tarjan(now, now);
115             }
116         }
117 
118         cut_cnt = cut.size();
119 
120         show_info();
121     }
122 }
123 
124 int main(){
125     
126     solve();    
127     //cout << "not error" << endl;
128     return 0;
129 }

 

上一篇:「图论」Tarjan


下一篇:强连通分量(缩点,tarjan)(无讲解)