【模板】强连通分量的kosaraju算法实现

来源:互联网 发布:淘宝网渔具店 编辑:程序博客网 时间:2024/06/05 16:09
  • 算法思想

    kosaraju算法通过对有向图进行两次Dfs得到强连通分量

    第一次dfs对每个节点进行标号,在回溯前给顶点标号,完成标号后,越接近图的尾部(搜索树的叶子),顶点的标号越小。相当于对强连通分量缩点后的DAG进行了一次拓扑排序。

    第二次反向dfs,也就是先将图中的边全部反向,在新的图中进行深搜。从标号大的节点(相当于拓扑排序中靠近根节点的节点)开始搜索.

    算法正确性的保证:参考:《挑战程序设计竞赛》p321

  • 例题: POJ2186

    • 题目大意

    N头牛,现在给出M组关系(A,B)表示A喜欢B,喜欢具有传递性,问有多少头牛是被所有牛喜欢。

    • 分析

    对于每个强连通分量中,牛一定是互相喜欢的,所以我们可以先找出所有的强连通分量再缩点。

    缩点后,如果一个节点能被所有节点走到,那么这个节点一定是叶子节点,出度为0(如果出度不为0必然为形成环),而且这样的节点只能有一个(因为这个节点不喜欢其他任何一个节点)

    • 代码
#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>#include<stack>using namespace std;const int MAXN=10005;const int MAXM=50005;int n,m;bool used[MAXN];int belong[MAXN];//所属强连通分量的拓扑序,belong[i]表示i属于哪个分支int out[MAXN];struct Edge{    int v;    int next;}edge[MAXM*2];int edgecount;int head[MAXN];int rhead[MAXN];void Init(){      edgecount=0;      memset(head,-1,sizeof(head));      memset(rhead,-1,sizeof(rhead));}void Add_edge1(int u,int v){    edge[++edgecount].v=v;    edge[edgecount].next=head[u];    head[u]=edgecount;}void Add_edge2(int u,int v){     edge[++edgecount].v=v;     edge[edgecount].next=rhead[u];     rhead[u]=edgecount;}stack<int>Q;void Dfs(int u){    used[u]=1;    for(int k=head[u];k!=-1;k=edge[k].next)    {          int v=edge[k].v;          if(!used[v])Dfs(v);    }    Q.push(u);}void rDfs(int u,int group){    used[u]=1;    belong[u]=group;    for(int k=rhead[u];k!=-1;k=edge[k].next)    {        int v=edge[k].v;        if(!used[v])rDfs(v,group);    }}int scc()//返回强连通分量个数{      int ans=0;      memset(used,0,sizeof(used));      while(!Q.empty())Q.pop();      for(int i=1;i<=n;i++)      {           if(!used[i])Dfs(i);      }      memset(used,0,sizeof(used));      while(!Q.empty())      {          int x=Q.top();          Q.pop();          if(!used[x]){ans++;rDfs(x,ans);}      }      return ans;}int main(){    int a,b;    Init();    while(scanf("%d%d",&n,&m)!=EOF)    {        memset(out,0,sizeof(out));        for(int i=1;i<=m;i++)        {            scanf("%d%d",&a,&b);            Add_edge1(a,b);            Add_edge2(b,a);        }        int scc_cnt=scc();        for(int u=1;u<=n;u++)        {               for(int k=head[u];k!=-1;k=edge[k].next)               {                    int v=edge[k].v;                    if(belong[u]!=belong[v])out[belong[u]]++;               }        }        int sum=0;//统计出度为0的点的块数        int x;//出度为0的连通分量编号        for(int i=1;i<=scc_cnt;i++)        {              if(out[i]==0){sum++;x=i;}        }        int ans=0;        for(int i=1;i<=n;i++)        {              if(belong[i]==x)ans++;        }        if(sum==1)cout<<ans<<endl;        else cout<<0<<endl;    }    return 0;}
原创粉丝点击