本文大量参考:http://www.nocow.cn/index.php/%E5%BC%BA%E8%BF%9E%E9%80%9A%E5%88%86%E9%87%8F
感谢:byvoid
首先,强连通分支是定义在一个有向图G上的。一个有向图的强连通分支就是一个最大的顶点集合,在这个集合中,所有的点都是互为可达的,即对于每一个点对(u,v)都能找到一条从u到v的通路,也都能找到一条从v到u的通路。对于一个有向图G,把G分解为各强连通分支是常用的操作,也有很多经典的算法。
首先介绍kosaraju算法,这个算法描述起来比较简单,效率也很高。算法基于DFS,首先对于图G做一次DFS并且求出G的拓扑排序。然后,将G求转置(图G的转置就是将有向图中的边反向),得到GT。对于GT再做一次DFS,其中遍历的顺序要遵循所得到的拓扑排序。最后,对第二次的DFS形成的深度遍历森林,写出对于的强连通分支(森林中的每一颗树就是一个强连通分支)。分析一下效率,DFS对与邻接表O(V+E),求转置O(E),因此,总的效率为O(V+E)
接下来要重点介绍的是tarjan算法。tarjan基于一次DFS和一个栈空间,过程中引入2个参数low[],dfn[],dfn[i]表示的是G中点i是第几个被访问的。low[i]表示的是图中点i,在栈中所能追溯到的最远的先祖点j的dfn值。当算法回溯到点i,如果dfn[i]=low[i],那么从栈顶到点i,就形成一个强连通分支。
下面是算法流程:
tarjan(u) { DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值 Stack.push(u) // 将节点u压入栈中 for each (u, v) in E // 枚举每一条边 if (v is not visted) // 如果节点v未被访问过 tarjan(v) // 继续向下找 Low[u] = min(Low[u], Low[v]) else if (v in S) // 如果节点v还在栈内 Low[u] = min(Low[u], DFN[v]) if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根 repeat v = S.pop // 将v退栈,为该强连通分量中一个顶点 print v until (u== v) }
值得注意的是low[i]的赋值,在dfs进入递归之前,线判断点j是否被访问,如果没有,就进入递归,递归回溯之后才开始给low[i]赋值,怎样赋值,取low[i]和low[j]中较小的一个。如果点i已经在栈中了,那不需要进入递归了,直接赋值。low[i]
取low[i]和dfn[j]中较小的一个(这里一只有个疑问,是否可以改成,low[i]=min(low[i],low[j]))。
最后在回溯到点i之后,判断dfn[i]是否和low[i]相等,如果相等,做输出操作
下面是具体代码:
#include<iostream> #include<string.h> #include<stack> using namespace std; class vertex { public: int t; vertex* next; vertex(int a):t(a) { this->next=NULL; } }; const int maximum=200; stack<int> s; int index=0; int dfn[maximum]; int low[maximum]; int state[maximum];//0:unvisited 1:instack 2:poped int belog[maximum]; vertex* v[maximum]; void scc_tarjan(int i) { int j; dfn[i]=low[i]=++index; state[i]=1; s.push(i); for(vertex *e=v[i]->next;e;e=e->next) { j=e->t; if(state[j]==0) { scc_tarjan(j); if(low[j]<low[i]) low[i]=low[j]; } else if(state[j]==1&&dfn[j]<low[i]) low[i]=dfn[j]; } if(dfn[i]==low[i]) { int a=-1; while(a!=i) { a=s.top(); s.pop(); state[a]=2; belog[a]=i; } belog[i]=i; } } void init() { for(int i=1;i<=6;i++) { vertex*s=new vertex(i); v[i]=s; } v[1]->next=new vertex(3); v[1]->next->next=new vertex(2); v[2]->next=new vertex(4); v[3]->next=new vertex(5); v[3]->next->next=new vertex(4); v[4]->next=new vertex(6); v[4]->next->next=new vertex(1); v[5]->next=new vertex(6); /*for(int i=1;i<=8;i++) { vertex*s=new vertex(i); v[i]=s; } v[1]->next=new vertex(4); v[2]->next=new vertex(1); v[2]->next->next=new vertex(5); v[3]->next=new vertex(2); v[4]->next=new vertex(2); v[5]->next=new vertex(4); v[6]->next=new vertex(3); v[6]->next->next=new vertex(5); v[7]->next=new vertex(6); v[7]->next->next=new vertex(8); v[8]->next=new vertex(6); v[8]->next->next=new vertex(7); */ } int main() { memset(state,0,sizeof(state)); memset(v,0,sizeof(v)); index=0; while(!s.empty()) { s.pop(); } init(); for(int i=1;i<=6;i++) { if(state[i]==0) scc_tarjan(i); } for(int i=1;i<=6;i++) { cout<<i<<" is belog to "<<belog[i]<<endl; } }
发表评论
-
图的割点tarjan---zoj_1119
2011-10-24 23:00 1228http://acm.zju.edu.cn/on ... -
BFS与双向BFS---zoj_1505
2011-10-09 17:14 1639http://acm.zju.edu.c ... -
静态treap+线段树---zoj_2112
2011-09-29 23:06 1707http://acm.zju.edu ... -
NIM博弈游戏,SG函数---zoj_3084,zoj_2083
2011-09-23 23:00 1313Nim游戏是两个人进行的游戏有如下规则: ... -
多重背包--zoj_2156
2011-09-14 11:10 859首先介绍经典的三种背包问题,0-1背包,完全 ... -
模式串匹配---KMP
2011-08-31 20:49 1275朴素的的模式串匹配算法通常要在向前移动文本指针 ... -
八数码问题(A*&&双向BFS)---zoj_1217
2011-08-30 22:13 1664题目地址:http://acm.zju.edu.cn/onli ... -
ac自动机以及它上面的DFA
2011-08-08 23:04 2476AC自动机(Aho-Corasick)主要用 ... -
二分图最大匹配(匈牙利算法)--zoj1516,1525,2223
2011-07-20 22:36 1182二分图G(E,V)将点集合V分成两个部分L,R ... -
网络最大流(EK)---zoj_1734
2011-07-19 16:34 2127网络最大流是图论中的一个典型问题,为了精确的定 ... -
trie和前缀检查---zoj_2876
2011-07-13 23:03 864trie树是一种多叉树,广泛用于字典检索。如 ... -
位图bitmap
2011-07-13 21:08 912bitmap是一种广泛使用的数据结构,主要用在 ... -
LAC和RMQ的转化---zoj_3195
2011-07-12 22:35 1071我擦。。纠结了好久啊,从SF到TLE,先总结2 ... -
LAC的tarjan(离线)算法---zoj_1141
2011-07-08 17:52 1654LCA就不解释了,这里主要再次复习一下LC ... -
笛卡尔树以及treap---zoj_2243
2011-07-07 15:52 2833zoj_2243笛卡 ... -
线段染色,矩形切割,离散化---zoj_2301,zoj_1128
2011-06-24 22:32 1361染色问题:在一维坐标轴上(最右端为M),有N ... -
线段树---zoj_1610
2011-06-22 21:06 742线段树是一种二叉树的拓展,在线段树每个节点中 ... -
快速排序(qsort)
2011-06-16 17:03 775快速排序是一种高效的非稳定排序,它的基本思路 ... -
斐波那契散列
2011-06-16 16:38 3071斐波那契散列法其实是一种特殊的乘法散列,先来看乘法 ... -
单源最短路径SPFA---zoj3146
2011-06-09 15:25 938针对Bellman-Ford算法效率比较低 ...
相关推荐
SCC-643AP协议设置(SW501): 监控协议拨码对照表
求有向图的强连通分量(scc)Tarjan算法.docx
IEEE-CLOUD-ICWS-SCC-SERVICES-2011-AdvanceProgram
为了解决焦粉的再利用问题,提高能源利用效率,缓解焦粉露天堆放造成的粉尘污染,采用无机矿物质和有机高分子化合物复配制得SCC-1型复合黏结剂用于焦粉成型生产型焦。研究了焦粉的成型工艺,对工艺路线和工艺参数进行了...
三星SCC-641云台协议三星SCC-641云台协议
资源来自pypi官网。 资源全名:ibm-scc-1.1.0.tar.gz
scc-broker-client SCC的客户端(用于水平可伸缩性)。
实现用于查找有向图的强连通分量的 Tarjan 算法。 在强连通分量 (SCC) 中,每个节点到每个其他节点都有一条路径。 SCC 是不相交的。 入度或出度为零或属于无环图的节点自己形成 SCC。 接受邻接矩阵作为输入。 为了...
CEA、SCC-Ag、NSE及SE-CAD在肺癌病理分型、分期中的临床价值.pdf
资源分类:Python库 所属语言:Python 资源全名:steelscript.scc-0.6rc3.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
三星SCC 641P的使用详细说明书
html-sass-scc-molengeek-siteweb html-sass-scc-molengeek-siteweb html-sass-scc-molengeek-siteweb
放电管是一种使用于设备输入端的高压保护元件。若其两端的电压高过其保护规格值时,其内部会出现短路现象,并吸收掉输入的过高压。放电管分为固体放电管和气体放电管,气体放电管又根据封装的不一样分为陶瓷气体放电管...
jsf1.2得源代码,包括jsf-api,jsf-ri,jsf-tools,jsf-doc等等。。。
PLC操作使用说明, 包含设置、操作、电路图及联网操作及温度扩展和上位机通讯。
scc-jobserver-example
scc-rest2-ci
UoS-SCC-19-20 Southamton大学的太空学员挑战者-COMP1202
scc-移动SCCM - 移动咨询控制系统
INM420 Git分配 ...git clone https://github.com/AmandaLutzTO/inm420-SCC-git-assignment.git 贡献 给我发送电子邮件,将您的GitHub帐户名添加为贡献者。 编辑样式表以设置您的姓名的样式。 执照