<学习笔记> tarjan 求强连通分量

时间复杂度 O(n+m)

dfs 求解。

定义 dfn[n]为n当前的时间戳,low[n]为n最早能追溯到的时间戳,可知

 1  for(int i=first[n];i;i=next[i])
 2  {
 3      if(!dfn[Rode[i].t])
 4      {
 5           Tarjan(Rode[i].t);
 6           low[n]=min(low[n],low[Rode[i].t]);
 7       }
 8       else
 9            if(Instack[Rode[i].t])  low[n]=min(low[n],dfn[Rode[i].t]);
10     }

 

若回溯时 dfn[n]==low[n],即找到了一个强连通分量。如图

注意一个点也会被看做强连通分量。

若有环套环的情况,tarjan求的是最大的那个。

另外判断Instack是为了避免这样的情况。

防止不是环中元素的点被计入环。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;

int N,M,a,b,c,cnt,top,index,scc_cnt;
int first[100010],next[200010];
int dfn[100010],low[100010],Instack[100010],Stack[100010];
int belong_scc[100010];
vector<int> scc[100010];

struct maple{
    int f,t,d;
}Rode[200010];

void Build(int f,int t,int d)
{
    Rode[++cnt]=(maple){f,t,d};
    next[cnt]=first[f];
    first[f]=cnt;
}

void Tarjan(int n)
{
    dfn[n]=low[n]=++index;
    Instack[n]=1;
    Stack[++top]=n;
    for(int i=first[n];i;i=next[i])
    {
        if(!dfn[Rode[i].t]) // 之前没有走过
        {
            Tarjan(Rode[i].t);
            low[n]=min(low[n],low[Rode[i].t]);
        }
        else
              if(Instack[Rode[i].t])  low[n]=min(low[n],dfn[Rode[i].t]);
    }
    if(dfn[n]==low[n]) // 是一个环
    {
        int j;
        ++scc_cnt;
        do{
              j=Stack[top--];
              Instack[j]=0;
              belong_scc[j]=scc_cnt;
              scc[scc_cnt].push_back(j);
        }while(j!=n);
    }
}

int main()
{
    scanf("%d%d",&N,&M);
    for(int i=1;i<=M;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        Build(a,b,c); 
    }
    Tarjan(1);
    return 0;
}


 

 

posted @ 2017-11-07 18:29  loi_maple  阅读(214)  评论(0编辑  收藏  举报