1>求强连通分量:
这个的思路大致就是每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号(即点u不经过其父节点能到达的最小dfn)。
可求图中强连通分量个数及内容。
代码如下(伪):
1 void tarjan(int x) 2 { 3 dfn[x]=low[x]=++cnt; 4 instack[x]=1; 5 s.push(x); 6 int y; 7 for(int i=head[x];i;i=e[i].nxt) 8 { 9 y=e[i].to; 10 if(!dfn[y]) 11 { 12 tarjan(y); 13 low[x]=min(low[x],low[y]); 14 } 15 else if(instack[y]) low[x]=min(low[x],dfn[y]); 16 } 17 y=0; 18 if(dfn[x]==low[x]) 19 { 20 tot++; 21 while(x!=y) 22 { 23 y=s.top(); 24 s.pop(); 25 instack[y]=0; 26 belong[y]=tot; 27 } 28 } 29 } 30 void solve() 31 { 32 tot=cnt=0; 33 memset(dfn,0,sizeof(dfn)); 34 For(i,1,n) 35 if(!dfn[i]) tarjan(i); 36 }
2>求割点、割边
这个的算法与上面相似,只是增加了对于割点、割边的判定。
具体判定方式为:
割点:判断顶点U是否为割点,用U顶点的dnf值和它的所有的孩子顶点的low值进行比较,如果存在至少一个孩子顶点V满足low[v] >= dnf[u],就说明顶点V访问顶点U的祖先顶点,必须通过顶点U,而不存在顶点V到顶点U祖先顶点的其它路径,所以顶点U就是一个割点。对于没有孩子顶点的顶点,显然不会是割点。
桥(割边):low[v] > dnf[u] 就说明V-U是桥。
代码如下(伪):
割点
1 void dfs(int x,int root) 2 { 3 int tot=0; 4 low[x]=dfn[x]=++id; 5 for(int i=head[x];i;i=a[i].next) 6 { 7 int y=a[i].y; 8 if(!dfn[y]) 9 { 10 dfs(y,root); 11 low[x]=min(low[x],low[y]); 12 if(low[y]>=dfn[x]&&x!=root) bz[x]=1; 13 if(x==root) tot++; 14 } 15 low[x]=min(low[x],dfn[y]); 16 } 17 if(x==root&&tot>=2) bz[root]=1; 18 } 19 void solve() 20 { 21 for(int i=1;i<=n;i++) 22 if(!dfn[i]) dfs(i,i); 23 for(int i=1;i<=n;i++) 24 if(bz[i]) ans++; 25 }
割边
(类似于割点,不贴代码了先咕着)
3>求点双、边双
边双:如果一个无向连通图中,没有割边,那么这个无向连通图就是一个边双连通图。
求法:tarjan求出所有割边,然后把割边去掉,就是一个个边双连通分量了。然后只要去掉割边进行dfs染色就行了。
点双:在一个无向连通图中,如果没有割点,那么它是点双连通图。如果只有两个点,那么也是点双。一个无向图中的极大点双连通子图就是点双连通分量。
求法:先咕着。。。
4>点双缩点、边双缩点
自己实现去,都求出点双边双了,就重新建个图的事。。。