强连通分量

强连通分量是对有向图才有的概念。

联通分量:对于分量中任意两点u,v,必然可以从u走到v,且从v走到u。

强连通分量:极大联通分量(不能再找到更多的点加入联通分量点集)。

联通分量可以将有向图缩点(将所有联通分量缩成一点)变为有向无环图(拓扑图),从而在新的拓扑图上递推(可以线性求最短路,最长路等)。

图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

tarjin算法求强连通分量

树枝边:DFS时经过的边,即DFS搜索树上的边。

前向边:与DFS方向一致,从某个结点指向其某个子孙的边。

后向边:与DFS方向相反,从某个结点指向其某个祖先的边。(返祖边)

横叉边:从某个结点指向搜索树中的另一子树中的某结点的边。

判断遍历当前点在某个强连通分量中:

1.存在后向边,指向祖先节点。

2.存在横叉边,从横叉边走到祖先节点。

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。 由定义可以得出,Low(u)=Min {Low(u), Low(v) } (u,v)为树枝边,u为v的父节点 . Low(u)=Min {Low(u), DFN(v) }DFN(v),(u,v)为指向栈中节点的后向边(指向栈中结点的横叉边) } 当结点u搜索结束后,若DFN(u)=Low(u)时,则以u为根的搜索子树上所有还在栈中的节点是一个强连通分量。

注意求完tarjin之后,建立新图 过程:

for(int i=1;i<=n;i++)
 for(i 的所有邻点j)
  if(i和j不在同一联通分量中)
   加一条 id[i]到id[j]的新边 

建立的新图按for(int i=scc_cnt;i>=1;i--)的顺序即为拓扑序。

简单说明:在tarjin算法中对于找到每一个强联通分量,当处理他们的时候,此时已经遍历完了子树的所有边,意味着此时强联通分量最高点已经没有出边。所有倒叙即为新图的拓扑序。

注意这点很重要(可以简化求拓扑序的过程)。

 代码:

 1 void tarjin(int u)
 2 {
 3     stk.push(u);//加入栈中 
 4     low[u]=dfn[u]=++times;//时间戳 
 5     is_stk[u]=true;//该元素已经进栈 
 6     for(int i=h[u];i!=-1;i=ne[i])
 7     {
 8         int j=e[i];
 9         if(!dfn[j])//子树未遍历就遍历 
10         {
11             tarjin(j);
12             low[u]=min(low[u],low[j]);
13         }
14         else if(is_stk[j]) low[u]=min(low[u],low[j]);//遍历到更早的点 
15     }
16     if(low[u]==dfn[u])//此时u是一个强联通分量高度最高的点 
17     {
18         int y;
19         ++scc_cnt;//强联通分量标记 
20         while(y!=u)
21         {
22             y=stk.top();
23             stk.pop();
24             is_stk[y]=false;//标记元素出栈 
25             siz[scc_cnt]++;//记录每个强连通分量大小 
26             id[y]=scc_cnt;//标记原图每个点属于哪个强连通分量 
27         }
28     }
29 }

建新图代码:

 1 unordered_set<ll>S;
 2 for(int i=1;i<=n;i++)
 3      for(int j=h[i];j!=-1;j=ne[j])
 4       {
 5           int k=e[j];
 6           int a=id[i],b=id[k];
 7           ll Hash=a*1000000ll+b;//如果题目要求不能建重边需要对边判重。
 8           if(a!=b&&S.count(Hash))
 9           {
10               add(hs,a,b);
11              S.insert(Hash);
12          }
13      }
原文地址:https://www.cnblogs.com/flyljz/p/13685412.html