Kosaraju算法的原理:先对逆图作一遍后序遍历,计算访问时间f[u](如果图是一个DAG(有向无环图),这一过程产生一个拓扑排序序列)。然后在原图上再一次DFS,不过是从f[u]最大的未访问点开始遍历。
这样得到的就是一个强连通分量了。因为当原图中w点可以到达v点的时候,在访问逆图的时,逆图中的dfs树中v才可能是w的父节点,这样,f[v]将大于f[w]。再在原图中作DFS,由于是优先访问具有最高后序编号的未访问的结点,如果在原图中v不可到达w,则v和w不可能在同一棵dfs树内,那么v和w不在同一连通分支中。就这么简单的方法。
注意到按照f[u]递减的顺序dfs实际上就是按照拓扑排序的逆序访问各顶点。
#include
<
iostream
>
using namespace std;
#define MAXN 10001
struct Edge{
int v,next;
}edg[ 10 * MAXN];
int p[MAXN],p1[MAXN],n,m,pcnt,post[MAXN],postcnt,c;
int visited[MAXN];
int cdeg[MAXN];
void dfs( int u){
int i,v;
for (i = p1[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
if ( ! visited[v]){
visited[v] = 1 ;
dfs(v);
}
}
post[postcnt ++ ] = u;
}
void dfs1( int u){
int i,v;
for (i = p[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
if ( ! visited[v]){
visited[v] = c;
dfs1(v);
}
}
}
int main(){
int i,j,u,v,cnt,ans,mark;
while (scanf( " %d%d " , & n, & m) != EOF){
memset(p, - 1 , sizeof (p));
memset(p1, - 1 , sizeof (p1));
pcnt = 0 ;
for (i = 0 ;i < m;i ++ ){
scanf( " %d%d " , & u, & v);
edg[pcnt].next = p[u];
edg[pcnt].v = v;
p[u] = pcnt ++ ;
edg[pcnt].next = p1[v];
edg[pcnt].v = u;
p1[v] = pcnt ++ ;
}
memset(visited, 0 , sizeof (visited)); // 反向图,求f[u]
postcnt = 0 ;
for (i = 1 ;i <= n;i ++ ){
if ( ! visited[i]){
visited[i] = 1 ;
dfs(i);
}
}
c = 0 ;
memset(visited, 0 , sizeof (visited)); // 正向图
for (i = postcnt - 1 ;i >= 0 ;i -- ){
if ( ! visited[post[i]]){
visited[post[i]] =++ c; // 标记各SCC
dfs1(post[i]);
}
}
memset(cdeg, 0 , sizeof (cdeg)); // 计算各SCC的出度
for (i = 1 ;i <= n;i ++ ){
for (j = p[i];j !=- 1 ;j = edg[j].next){
if (visited[i] != visited[edg[j].v]){
cdeg[visited[i]] ++ ;
}
}
}
cnt = 0 ;
for (i = 1 ;i <= c;i ++ ){ // 计算出度为0的SCC个数
if (cdeg[i] == 0 ){
cnt ++ ;
mark = i;
}
}
if (cnt > 1 ){
printf( " 0\n " );
continue ;
}
ans = 0 ;
for (i = 1 ;i <= n;i ++ ){
if (visited[i] == mark)
ans ++ ;
}
printf( " %d\n " ,ans);
}
return 0 ;
}
using namespace std;
#define MAXN 10001
struct Edge{
int v,next;
}edg[ 10 * MAXN];
int p[MAXN],p1[MAXN],n,m,pcnt,post[MAXN],postcnt,c;
int visited[MAXN];
int cdeg[MAXN];
void dfs( int u){
int i,v;
for (i = p1[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
if ( ! visited[v]){
visited[v] = 1 ;
dfs(v);
}
}
post[postcnt ++ ] = u;
}
void dfs1( int u){
int i,v;
for (i = p[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
if ( ! visited[v]){
visited[v] = c;
dfs1(v);
}
}
}
int main(){
int i,j,u,v,cnt,ans,mark;
while (scanf( " %d%d " , & n, & m) != EOF){
memset(p, - 1 , sizeof (p));
memset(p1, - 1 , sizeof (p1));
pcnt = 0 ;
for (i = 0 ;i < m;i ++ ){
scanf( " %d%d " , & u, & v);
edg[pcnt].next = p[u];
edg[pcnt].v = v;
p[u] = pcnt ++ ;
edg[pcnt].next = p1[v];
edg[pcnt].v = u;
p1[v] = pcnt ++ ;
}
memset(visited, 0 , sizeof (visited)); // 反向图,求f[u]
postcnt = 0 ;
for (i = 1 ;i <= n;i ++ ){
if ( ! visited[i]){
visited[i] = 1 ;
dfs(i);
}
}
c = 0 ;
memset(visited, 0 , sizeof (visited)); // 正向图
for (i = postcnt - 1 ;i >= 0 ;i -- ){
if ( ! visited[post[i]]){
visited[post[i]] =++ c; // 标记各SCC
dfs1(post[i]);
}
}
memset(cdeg, 0 , sizeof (cdeg)); // 计算各SCC的出度
for (i = 1 ;i <= n;i ++ ){
for (j = p[i];j !=- 1 ;j = edg[j].next){
if (visited[i] != visited[edg[j].v]){
cdeg[visited[i]] ++ ;
}
}
}
cnt = 0 ;
for (i = 1 ;i <= c;i ++ ){ // 计算出度为0的SCC个数
if (cdeg[i] == 0 ){
cnt ++ ;
mark = i;
}
}
if (cnt > 1 ){
printf( " 0\n " );
continue ;
}
ans = 0 ;
for (i = 1 ;i <= n;i ++ ){
if (visited[i] == mark)
ans ++ ;
}
printf( " %d\n " ,ans);
}
return 0 ;
}