Tarjan算法
这是一个求一个图中的强连通分量的算法。强连通分量是指这一个子图中所有节点都能互相到达。当然,Tarjan的效率很高,时间复杂度为O(n+m)。
这里我们要用到两个关键的数组,一个是dfn,简称豆腐脑,是记录每一个节点在dfs中是第几个被搜到的(时间戳),还有一个就是low,代表了每一个节点的根,就是它的强连通分量的起始点。
当一个点没有被搜到过,就继续往下搜,并且更新走到自己身上的点的dfn。当一个点第二次被找到时,证明它已经在一个强连通分量里了,所以就要认找到你的人为根(更新low)
当一个点一次dfs过后还没被搜到,就以这个点继续搜。
具体代码如下
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())s=(s<<3)+(s<<1)+(c^48);
return s*f;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9) write(x/10);
putchar(x%10+'0');
}
//读入输出优化
int dfn[10001],low[10001],n,m,ans=0; //dfn:每个点第几个被搜到的(时间戳)
//low: 作为每个点在这颗树中的,最小的子树的根,每次保证最小,like它的父亲结点的时间戳这种感觉。如果它自己的LOW[]最小,那这个点就应该重新分配,变成这个强连通分量子树的根节点。
int visit[10001],tot; //visit:有没有被搜到过的标记。tot:现在是第几个单位时间
vector<int>v[10001];//邻接表
stack<int>s;//栈
void tarjan(int x)
{
dfn[x]=low[x]=++tot; //将找到的这个点的dfn和low都改成 当前时间戳。
s.push(x); //入栈
visit[x]=1;//标记
for(int i=0;i<v[x].size();i++)
{
if(!dfn[v[x][i]]) { //当前点没有被找到过(没有定义过dfn等同于没有被找到过)
tarjan(v[x][i]); //继续往下搜
low[x]=min(low[x],low[v[x][i]]); //更新low
}
else if(visit[v[x][i]]){ //当前点被找到过了
low[x]=min(low[x],dfn[v[x][i]]); //用搜到的点的dfn去更新当前点的low
}
}
if(low[x]==dfn[x]) //如果当前点就是自己的根
{
int t=0;
while(s.top()!=x){
t++;
visit[s.top()]=0;s.pop();
}//出栈
s.pop();visit[x]=0;
if(t)ans++;//强连通分量个数加一
}
return;
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
v[x].push_back(y);//建邻接表
}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);//如果当前点没被找到过就以当前点为中心去tarjan
printf("%d\n",ans);
return 0;
}
下面是一段玄学的TarjanAC代码
#include<bits/stdc++.h>
#define ______________________________ int
#define _____________________________ 10001
using namespace std;
inline ______________________________ read(){
______________________________ ___=0,_=1;char _______________________=getchar();
while(!isdigit(_______________________)){if(_______________________=='-')_=-1;_______________________=getchar();}
while(isdigit(_______________________)){___=___*10+_______________________-'0';_______________________=getchar();}
return ___*_;
}
inline void write(______________________________ __________________){
if(__________________<0){putchar('-');__________________=-__________________;}
if(__________________>9)write(__________________/10);
putchar(__________________%10+'0');
}
______________________________ ____________[_____________________________],_____________[_____________________________],n,m,________________=0;
______________________________ ______________[_____________________________],_________________________,________________________;
vector<______________________________>_____[_____________________________];
stack<______________________________>___;
void _______________(______________________________ __________________)
{
____________[__________________]=_____________[__________________]=++________________________;
___.push(__________________);
______________[__________________]=1;
for(______________________________ i=0;i<_____[__________________].size();i++)
{
if(!____________[_____[__________________][i]]) {
_______________(_____[__________________][i]);
_____________[__________________]=min(_____________[__________________],_____________[_____[__________________][i]]);
}
else if(______________[_____[__________________][i]]){
_____________[__________________]=min(_____________[__________________],____________[_____[__________________][i]]);
}
}
if(_____________[__________________]==____________[__________________])
{
______________________________ t=0;
while(___.top()!=__________________){
t++;
______________[___.top()]=0;___.pop();
}
___.pop();______________[__________________]=0;
if(t)________________++;
}
return;
}
______________________________ main()
{
n=read();m=read();
for(______________________________ i=1;i<=m;i++)
{
______________________________ __________________=read(),y=read();
_____[__________________].push_back(y);
}
for(______________________________ i=1;i<=n;i++)if(!____________[i])_______________(i);
printf("%d\n",________________);
return 0;
}