tarjan求强连通分量模板

什么是强连通分量?

百度百科

有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

实际上构成一个环的节点都可以叫做强连通分量,特别的单独的一个节点也可以叫做强连通分量

怎么实现tarjan?

tarjan算法中的数组

dfn(遍历的编号数组) low(遍历的最小的父节点) vis(记录数组) color(染色数组) stack(栈)

具体做法(dfs)

①把节点放入栈并且初始化dfn和low相等等于当前的节点,使vis记录这个节点

②遍历当前节点的临近节点并判断

(1)如果这个节点并没有被遍历过,dfs这个节点在回溯的时候设置求low数组

公式:low[n]=min(low[n],low[p])其中n是传入的形参,p是临近的节点

(2)如果这个节点被遍历过了,但是还没来得及入栈,那么计算一下low数组

③染色

(1)如果最终一个节点的dfn和low是一样的话,那么说明这个节点可以成为是一个强连通的头节点

(2)去除栈帧,把每个头结点上的栈都染成相同的颜色。

(3)注意这个时候需要去除vis标记

(4)注意如果栈到了n这个节点也得把这个节点提出栈才可以

代码

#include <bits/stdc++.h>
using namespace std;
map<int,int> dfn,low,vis,color;
stack<int> st;
int sum,col;
vector<int> G[1005];
void tarjan(int n)
{
  dfn[n]=low[n]=++sum;
  vis[n]=1;
  st.push(n);
  for(int i=0;i<G[n].size();i++)
  {
    int p=G[n][i];
    if(!dfn[p])
    {
      tarjan(p);
      low[n]=min(low[n],low[p]);
    }
    else if(vis[p])
    low[n]=min(low[n],low[p]);
  }
  if(low[n]==dfn[n])
  {
    color[n]=++col;
    vis[n]=0;
    while(st.top()!=n)
    {
      color[st.top()]=col;
      vis[st.top()]=0;
      st.pop();
    }
    st.pop();
  }
}
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n,m;
  cin>>n>>m;
  while(m--)
  {
    int t1,t2;
    cin>>t1>>t2;
    G[t1].push_back(t2);
  }
  for(int i=1;i<=n;i++)
  if(!dfn[i])
  tarjan(i);
  for(int i=1;i<=n;i++)
  cout<<color[i]<<" ";
}
posted @ 2018-12-03 20:50  baccano!  阅读(353)  评论(0编辑  收藏  举报