对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号。定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点(这个好像有点问题,原文评论说改为:low(u)为u或u的子树通过最多一条反向边能够追溯到的最早的栈中节点的次序号。好像对?)。根据定义,则有:
Low(u)=Min{DFS(u)DFS(v) (u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点Low(v) (u,v)为树枝边(父子边)}
/*复杂度为O(m+n)*/ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MOD 100000 #define inf 1<<29 #define LL long long #define MAXN 20010 #define MAXM 50010 using namespace std; struct Edge { int to,next; } edge[MAXM*2]; int head[MAXM*2],tot; int low[MAXN],DFN[MAXN],belong[MAXN];///belong 的值为1-scc int index,top; int scc ; ///强连通分量 bool inStack[MAXN]; int num[MAXN],stack[MAXN]; ///num为各个强连通分量包含的点的个数,数组编号1-scc num数组不一定需要,结合实际情况 void addedge(int u,int v) { edge[tot].to = v;low edge[tot].next = head[u]; head[u] = tot++ ; } void Tarjan(int u) ///遍历当前点u的邻接所有点v { int v; low[u] = DFN[u] = ++index; stack[top++] = u; inStack[u] = true; for(int i=head[u] ; i!=-1 ; i=edge[i].next) { v = edge[i].to; if( !DFN[v] ) ///当前点v没有被标号 { Tarjan(v); if(low[u]>low[v]) low[u] = low[v]; } else if( inStack[v] && low[u] > DFN[v]) ///当前点v之前被标记过 <a target=_blank href="http://blog.csdn.net/u014665013/article/details/51351371">后向边</a> low[u] = DFN[v]; ///这里可以是low[u] = DFN[v] 不是Low[v] } if(low[u] == DFN[u]) ///找到一个强连通分量 { scc++; do { v=stack[--top]; ///清空当前强连通分量栈 必须清空 inStack[v] = false; belong[v]=scc; num[scc]++; } while(v!=u); } } void solve(int N) { for(int i=1; i<=N ; i++) if( !DFN[i] ) Tarjan(i); } void init() ///如果数组太大没必要全部初始化 { tot = index = scc = top =0; memset(DFN,0,sizeof(DFN)); memset(inStack,false,sizeof(inStack)); memset(num,0,sizeof(num)); memset(head,-1,sizeof(head)); } int main () { int n,m,Case=1; while(~scanf("%d%d",&n,&m)) { int u,v; init(); for(int i=0; i<m; i++) { scanf("%d%d",&u,&v); addedge(u,v); // addedge(v,u); } solve(n); printf("强连通分量为:%d\n",scc); } return 0; }