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]<<" ";
}