Kosaraju算法——强连通分量

´有向图的极大强连通子图,称为强连通分量。
´子图指的是选取V的一个子集V’,以及E当中所有满足u,v∈V’的边集E’所指代的图.
´我们需要找出一幅有向图当中的所有强连通分量。
 
´一个最朴素的算法:
´构造一个传递闭包(也就是数组Aij表示i能否到达j),然后把Aij=Aji=1的节点置于同一个强连通分量当中
´这个算法的复杂度是O(n^3),优点是代码复杂度小,缺点是速度太慢了
 
´这个算法原理和上面的方法是类似的,如果A能到达B并且B能到达A,那么A和B在同一个强连通分量里面。
´也可以等同于说,如果原图与逆图中A都能到达B,那么A与B在同一个强连通分量里面。
 
´逆图,指的是将原本有向图的所有边的方向变成相反,也就是说原本从a到b的边变成从b到a。
 
´算法流程有三步:
´对原图进行DFS,求出每一个节点的结束时间戳(离开这个节点时访问了多少个节点);
´选择结束时间戳最晚的节点,在逆图中进行遍历,删除能遍历到的节点,这些节点构成一个强连通分量;
´如果还有顶点未删除,重复第二步;
#include <bits/stdc++.h>
using namespace std;

const int maxn=100000+15;
int n,m;
vector <int> edge[maxn],rev[maxn];
int tim,rank[maxn],ref[maxn];
bool boo[maxn];
int color[maxn],tot;
int dfs1(int now)
{
    if (boo[now]) return 0;
    boo[now]=true;
    for (int i=0;i<edge[now].size();dfs1(edge[now][i]),i++);
    rank[now]=++tim;
    ref[tim]=now;
    return 0;
}
int dfs2(int now)
{
    if (color[now]!=0) return 0;
    color[now]=tot;
    for (int i=0;i<rev[now].size();dfs2(rev[now][i]),i++);
    return 0;
}
int main()
{
    freopen("ko.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        rev[y].push_back(x);
        }
    for (int i=1;i<=n;i++)
     if (!boo[i]) dfs1(i);
    for (int i=n;i>=1;i--)
     if (color[ref[i]]==0)
     {
            tot++;
            dfs2(ref[i]);
            }
    for (int i=1;i<=n;i++) 
     printf("%d ",color[i]);
    return 0;
}

 

posted @ 2017-01-25 16:34  Mr.9Pounds15Pence  阅读(180)  评论(0编辑  收藏  举报