先说一下割点跟割边吧。
割桥就是如果一个连通图里删除这条边之后,这个图会变成两个连通图,那么这条边就称之为割桥。
这是割桥的代码,里面呆着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 }
割点就是如果一个连通图删除了某个点和相关的边之后那么,这个图就是变成两个连通图, 这个点叫做割点。
这是割点代码
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 }
以上是无向图的代码,有向图的自己改一下吧~
这是对于有向图的强连通分量,不多讲。想详细了解一下的话可以看这个: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 }
不考虑重边、
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 }
对于双连通分支,包括点连通和边连通。
对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足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 }
点连通(好吧,这个代码其实是白书上的。明天自己写一个= =。太困了。。。)
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 }
过两天把这几次的题搞上去吧~