有向图的强连通分量

有向图的强连通分量

1.Tarjan

O(V+E)

dfn[u]表示dfs时达到顶点u的次序号(时间戳),low[u]表示以u为根节点的dfs树中次序号最小的顶点的次序号,所以当dfn[u]=low[u]时,以u为根的搜索子树上所有节点是一个强连通分量。 先将顶点u入栈,dfn[u]=low[u]=++idx,扫描u能到达的顶点v,如果v没有被访问过,则dfs(v),low[u]=min(low[u],low[v]),如果v在栈里,low[u]=min(low[u],dfn[v]),扫描完v以后,如果dfn[u]=low[u],则将u及其以上顶点出栈。

# include<bits/stdc++.h>
using namespace std;

const int MAXN=2e4+100;
vector<int> G[MAXN];
stack<int> S;
int dfn[MAXN],low[MAXN],sccno[MAXN],dfs_clock,scc_cnt;
void dfs(int u)
{
   dfn[u]=low[u]=++dfs_clock;
   S.push(u);
   int len=G[u].size();
   for(int i=0;i<len;++i){
       int v=G[u][i];
       if(!dfn[v]){
           dfs(v);
           low[u]=min(low[u],low[v]);
      }else if(!sccno[v]){
           low[u]=min(low[u],dfn[v]);
      }
  }
   if(low[u]==dfn[u]){
       scc_cnt++;
       for(;;){
           int x=S.top(); S.pop();
           sccno[x]=scc_cnt;
           if(x==u) break;
      }
  }
}
void find_scc(int n)
{
   dfs_clock=scc_cnt=0;
   memset(sccno,0,sizeof(sccno));
   memset(dfn,0,sizeof(dfn));
   for(int i=1;i<=n;++i){
       if(!dfn[i]) dfs(i);
  }
}

int scc_du[MAXN];
queue<int> Qu;
int main()
{
   int n,m; scanf("%d%d",&n,&m);
   while(m--){
       int u,v; scanf("%d%d",&u,&v);
       G[u].push_back(v);
  }
   find_scc(n);
   for(int i=1;i<=n;++i){
       int len=G[i].size();
       for(int j=0;j<len;++j){
           if(sccno[i]!=sccno[G[i][j]]){
               scc_du[sccno[i]]++;
          }
      }
  }
   for(int i=1;i<=scc_cnt;++i){
       if(scc_du[i]==0) Qu.push(i);
  }
   int ans=0;
   if(Qu.size()!=1){
       printf("0\n");
  }else{
       int id=Qu.front();
       for(int i=1;i<=n;++i){
           if(sccno[i]==id) ++ans;
      }
       printf("%d\n",ans);
  }
   return 0;
}
/*
3 3
1 2
2 1
2 3
*/

缩点:

if(sccno[x]==sccno[y]) continue;
else add(sccno[x],sccno[y]);

2.korasaju

O(V+E)

先对正图进行一遍dfs,遇到没访问过的点就让其发现时间等于目前的dfs次序号。在回溯时若发现某一结点的子树全部被遍历完,就让其完成时间等于目前dfs次序号。正图遍历完后将节点按完成时间入栈,保证栈顶是完成时间最大的节点,栈底是完成时间最小的节点。然后从栈顶开始向下每一个没有被反向遍历过的节点为起点对逆图进行一遍dfs,将访问到的点记录下来(或染色)并弹栈,每一遍反向dfs遍历到的点就构成一个强连通分量。

const int MAXN=2e4+100;
vector<int> G[MAXN],G2[MAXN];
vector<int> S;
int vis[MAXN],sccno[MAXN],scc_cnt;
void dfs1(int n)
{
   if(vis[u]) return ;
   vis[u]=1;
   for(int i=0;i<G[u].size();++i) dfs1(G[u][i]);
   S.push_back(u);
}
void dfs2(int u)
{
   if(sccno[u]) return ;
   sccno[u]=scc_cnt;
   for(int i=0;i<G2[u].size();++i) dfs2(G2[u][i]);
}
void find_scc(int n)
{
   scc_cnt=0;
   S.clear();
   memset(sccno,0,sizeof(sccno));
   memset(vis,0,sizeof(vis));
   for(int i=0;i<n;++i) dfs1(i);
   for(int i=n-1;i>=0;--i){
       if(!sccno[S[i]]){
           scc_cnt++;
           dfs2(S[i]);
      }
  }
}



posted @ 2022-02-27 00:04  fengzlj  阅读(73)  评论(0)    收藏  举报