tarjan总结

先说一下割点跟割边吧。

割桥就是如果一个连通图里删除这条边之后,这个图会变成两个连通图,那么这条边就称之为割桥。

这是割桥的代码,里面呆着lca求法。

割点和割桥的就是用一个时间戳和回到祖先确定。

用dfs的时间戳可以看出。

割点为low[v] >= dfn[u]

割边为low[v] > dfn[u]。

但是要注意的是割点的条件对于搜索树的根节点是要特殊处理的,当根节点的孩子大于1的时候就一定是割点。

 1 void tarjan(int u,int pre)
 2 {
 3     int v,i,j;
 4     dfn[u] = low[u] = ++dfsclock;
 5     loop(0,i,g[u].size())
 6     {
 7         v = g[u][i];
 8         if(!dfn[v])//保证是树枝边
 9         {
10             tarjan(v,u);
11             father[v] = u;//v的父亲是u
12             low[u] = min(low[v],low[u]);
13             if(low[v] > dfn[u])//加入v回到的祖先小于u那么u,v一定是桥。
14             cut++;
15             else
16             merge(u,v);
17         }
18         else if(v != pre)//保证不是直向自己父亲的遍
19         low[u] = min(low[u],dfn[v]);
20     }
21 }
22 void lca(int u,int v)//最后的u和v是同一个最近祖先
23 {
24 
25     while(u != v)
26     {
27         while(dfn[u] >= dfn[v] && u != v)//v遍历的比较早
28         {
29             if(merge(u,father[u]))//如果u的父亲和u不是在一个环里也就是不在缩点之后的同一个点,那么就是缩点之后的桥
30             cut--;
31             u = father[u];
32         }
33         while(dfn[v] >= dfn[u] && u != v)
34         {
35             if(merge(v,father[v]))
36             cut--;
37             v =  father[v];
38         }
39     }
40 }
View Code

割点就是如果一个连通图删除了某个点和相关的边之后那么,这个图就是变成两个连通图, 这个点叫做割点。

这是割点代码

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <stdlib.h>
 6 #include <vector>
 7 #include <queue>
 8 #include <stack>
 9 #define loop(s,i,n) for(i = s;i < n;i++)
10 #define cl(a,b) memset(a,b,sizeof(a))
11 using namespace std;
12 const int maxn = 205;
13 vector<int>g[maxn];
14 int is_cut[maxn],dfn[maxn],low[maxn];
15 
16 int dfsclock;
17 int cut;
18 void init(int n)
19 {
20     int i;
21     for(i = 0;i <= n;i++)
22     g[i].clear();
23     dfsclock = 0;
24     cl(is_cut,0);
25     cl(low,0);
26     cl(dfn,0);
27     cut = 0;
28     return ;
29 }
30 void tarjan(int u,int pre)
31 {
32     dfn[u] = low[u] = ++dfsclock;
33     int i;
34     int child = 0;
35     for(i = 0;i < g[u].size();i++)
36     {
37         int v;
38         v = g[u][i];
39 
40         if(!dfn[v])//保证是没访问过的树枝边。
41         {
42             child++;//这里要加一个孩子数,为的是后面对树根的判断。
43             tarjan(v,u);
44             if(low[v] < low[u])
45             low[u] = low[v];
46             if(low[v] >= dfn[u])//注意区别。割点与割桥代码差的很少,就这么一点。割点的自己点是可以回到自身的,但是割边不行假如回到了,那么说明是环就一定不是桥~
47             is_cut[u] = 1;
48         }
49         else if(v != pre)
50         low[u] = min(dfn[v],low[u]);
51     }
52     if(pre < 0 && child == 1)//树根的判断。
53     is_cut[u] = 0;
54 
55     return ;
56 }
57 
58 int main()
59 {
60     int u,v,n;
61     while(scanf("%d",&n)&&n)
62     {
63         init(n);//我觉得我这个人活在世上就是为了考验我容忍傻逼的限度。老忘初始化~
64 
65         read();//就是见图过程,不谢了。
66         tarjan(1,-1);//如果不是一个连通图,那么就for一下对每一个!dfn[i]做一次。
67         int i;
68         for(i = 1;i <= n;i++)
69         {
70             if(is_cut[i])
71             cut++;
72         }
73         cout<<cut<<endl;
74     }
75     return 0;
76 }
View Code

 以上是无向图的代码,有向图的自己改一下吧~

这是对于有向图的强连通分量,不多讲。想详细了解一下的话可以看这个:https://www.byvoid.com/blog/scc-tarjan

贴下自己的代码,注意对重边情况要另外写哦~

这是由重边情况的。

 1 const int maxn = 200005;
 2 bool vis[2000005];
 3 struct edge
 4 {
 5     int v,next;
 6     edge()
 7     {
 8         next = -1;
 9     }
10 }edges[maxn*10-45];
11 struct ed
12 {
13     int u,v;
14 }e[maxn*10-45];
15 int dfn[200005],low[200005],belong[200005];
16 bool inst[200005];
17 int g[maxn];
18 vector<int>ng[maxn];
19 
20 stack<int>st;
21 int bcnt,cnt,time;
22 
23 void init(int n)
24 {
25     int i;
26     for(i =0;i <= n;i++)
27     g[i] = -1;
28     time = 0;bcnt = cnt = 0;
29     return ;
30 }
31 void addedge(int u,int v,int val)
32 {
33     struct edge o;
34     edges[cnt].v = v;
35     edges[cnt].next = g[u];
36     g[u] = cnt;
37     cnt++;
38 
39     return ;
40 }
41 void tarjan(int i)
42 {
43     int j;
44     dfn[i] = low[i] = ++time;
45     inst[i] = 1;
46     st.push(i);
47 
48     for(j = g[i];j != -1;j = edges[j].next)
49     {
50         if(vis[j]) continue;
51         vis[j] = vis[j^1] = 1;
52         int v;
53         v = edges[j].v;
54         if(!dfn[v])
55         {
56             tarjan(v);
57             low[i] = min(low[i],low[v]);
58         }
59         else if(inst[v])
60         low[i] = min(low[i],dfn[v]);
61     }
62     int k;
63     if(dfn[i] == low[i])
64     {
65 
66         bcnt++;
67         do
68         {
69             k = st.top();
70             st.pop();
71             inst[k] = 0;
72             belong[k] = bcnt;
73 
74         }
75         while(k != i);
76     }
77 
78 }
79 void tarjans(int n)
80 {
81     int i;
82     bcnt = time = 0;
83     while(!st.empty())st.pop();
84     memset(dfn,0,sizeof(dfn));
85 
86     memset(inst,0,sizeof(inst));
87     memset(belong,0,sizeof(belong));
88     for(i = 1;i <= n;i++)
89     if(!dfn[i])tarjan(i);
90 }
View Code

不考虑重边、

 1 struct edge
 2 {
 3     int u,v,val;
 4 };
 5 int dfn[10050],low[10050],belong[10050],inst[10050];
 6 int pnum[10050];
 7 int in[10050];
 8 int inside[10050];
 9 int out[10050];
10 vector<edge> edges,es;
11 vector<int>g[maxn];
12 vector<int>ng[maxn];
13 stack<int>st;
14 int bcnt,cnt,dfsclock;
15 int max(int a,int b)
16 {
17     if(a > b)
18     return a;
19 
20     return b;
21 }
22 void init(int n)
23 {
24     int i;
25     for(i =0;i <= n;i++)
26     g[i].clear();
27 
28     edges.clear();
29 
30     es.clear();
31     dfsclock = 0;bcnt = cnt = 0;
32     return ;
33 }
34 void addedge(int u,int v,int val)
35 {
36     edges.push_back((edge){u,v,1});
37     g[u].push_back(cnt);
38     cnt++;
39 
40     return ;
41 }
42 void tarjan(int i)
43 {
44     int j;
45     dfn[i] = low[i] = ++dfsclock;
46     inst[i] = 1;
47     st.push(i);
48 
49     for(j = 0;j < g[i].size();j++)
50     {
51         edge e;
52         e = edges[g[i][j]];
53         int v;
54         v = e.v;
55         if(!dfn[v])
56         {
57             tarjan(v);
58             low[i] = min(low[i],low[v]);
59         }
60         else if(inst[v] && dfn[v] < low[i])
61         low[i] = dfn[v];
62     }
63     if(dfn[i] == low[i])
64     {
65         bcnt++;
66         do
67         {
68             j = st.top();
69             st.pop();
70             inst[j] = 0;
71             belong[j] = bcnt;
72 
73         }
74         while(j != i);
75     }
76 
77 }
78 void tarjans(int n)
79 {
80     int i;
81     bcnt = dfsclock = 0;
82     while(!st.empty())st.pop();
83     memset(dfn,0,sizeof(dfn));
84 
85     memset(inst,0,sizeof(inst));
86     memset(belong,0,sizeof(belong));
87     for(i = 1;i <= n;i++)
88     if(!dfn[i])tarjan(i);
89 }
View Code

 对于双连通分支,包括点连通和边连通。

对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支。

    对于边双连通分支,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。

我的代码(太困了,先贴上睡觉去)

边连通

 1 void tarjan(int u,int pre)
 2 {
 3     int v,i,j;
 4     dfn[u] = low[u] = ++dfsclock;
 5     s.push(u);
 6     loop(0,i,g[u].size())
 7     {
 8         v = g[u][i];
 9 
10         if(v != pre)
11         {
12             if(!dfn[v])//保证是树枝边
13             {
14                 tarjan(v,u);
15 
16                 low[u] = min(low[v],low[u]);
17 
18             }
19             else if(dfn[v] < low[u])
20             low[u] = dfn[v];
21         }
22 
23     }
24     if(low[u] ==dfn[u])
25     {
26         bcc_cnt++;
27         int t;
28         do
29         {
30 
31             t = s.top();
32             s.pop();
33             belong[t] = bcc_cnt;
34         }
35         while(t != u);
36     }
37 }
38 
39 
40 void find_bcc(int n)
41 {
42     int i;
43     memset(dfn,0,sizeof(dfn));
44     memset(low,0,sizeof(low));
45 
46     memset(belong,0,sizeof(belong));
47     while(!s.empty())s.pop();
48     dfsclock = bcc_cnt = 0;
49     loop(1,i,n)
50     if(!dfn[i]) tarjan(i,-1);
51     // puts("yes");
52     // printf("%d  """"""
",bcc_cnt);
53 }
View Code

点连通(好吧,这个代码其实是白书上的。明天自己写一个= =。太困了。。。)

 1 int dfs(int u,int fa)
 2 {
 3    int lowu= pre[u]=++dfsclock;
 4    int child=0;
 5     for(int i=0;i<g[u].size();i++)
 6     {
 7         int v=g[u][i];
 8         edge e;
 9         e.u=u,e.v=v;
10         if(!pre[v])
11         {
12             st.push(e);
13             child++;
14             int lowv=dfs(v,u);
15             lowu=min(lowu,lowv);
16             if(lowv>=pre[u])
17             {
18                 iscut[u]=1;
19                 bcc_cnt++;
20                 bcc[bcc_cnt].clear();
21                 for(;;)
22                 {
23                     edge x=st.top();st.pop();
24                     if(bccno[x.u]!=bcc_cnt)
25                     {
26                         bcc[bcc_cnt].push_back(x.u);
27                         bccno[x.u]=bcc_cnt;
28                     }
29                     if(bccno[x.v]!=bcc_cnt)
30                     {
31                         bcc[bcc_cnt].push_back(x.v);
32                         bccno[x.v]=bcc_cnt;
33                     }
34                     if(x.u==u&&x.v==v)break;
35                 }
36             }
37         }
38         else if(pre[v]<pre[u]&&v!=fa)
39         {
40             st.push(e);
41             lowu=min(lowu,pre[v]);
42         }
43     }
44     if(fa<0&&child==1)iscut[u]=0;
45     return lowu;
46 }
47 void find_bcc(int n)
48 {
49     int i;
50     memset(pre,0,sizeof(pre));
51     cl(iscut,0);
52     cl(bccno,0);
53     dfsclock = bcc_cnt = 0;
54     loop(0,i,n)
55     if(!pre[i]) dfs(i,-1);
56    // puts("yes");
57    // printf("%d  """"""
",bcc_cnt);
58 }
View Code

过两天把这几次的题搞上去吧~

原文地址:https://www.cnblogs.com/0803yijia/p/3251663.html