Tarjan算法 学习笔记

Tarjan

Tarjan 是一种可以求有向图强连通分量的算法,它能做到线性时间的复杂度。

学习博客

学习之前要先记住两个关键数组:

low[i] 表示栈中元素可以到达 i 的最小位置(也就是最靠近栈底);

dfn[i] 表示 i 元素在遍历过程中的标号(就是dfs遍历,第几个遍历到);

个人认为这个算法最关键的是理解当 dfn[i]==low[i] 时,在 i 元素后面入栈的都是强连通分量(也可称为强连通子图);

在这里插入图片描述
可以先从没有出度的点开始理解,比如3号点,这个点的dfn[3]=3,low[3]=3,并且不会再往下遍历,也就是说3号点的dfn和low都不会再改变,说明dfn[3] == low[3],3号点就是一个单独的强连通分量;
而其它的各个点(除了一号点),他们虽然的刚开始的 low==dfn,但是之后会进行改变,直到一号点;

再说回为啥 i 元素后面入栈的都是强连通分量,可以这样想,i 后面的元素都是和 i 相连的(也可以说 i 可以到达它们),当 i 都可以自己到自己时,那 i 可以到的点是不是也可以到达 i 呢?

模板:

void Tarjan ( int x ) {
         dfn[ x ] = ++dfs_num ;
         low[ x ] = dfs_num ;
         vis [ x ] = true ;//是否在栈中
         stack [ ++top ] = x ;
         for ( int i=head[ x ] ; i!=0 ; i=e[i].next ){
                  int temp = e[ i ].to ;
                  if ( !dfn[ temp ] ){
                           Tarjan ( temp ) ;
                           low[ x ] = gmin ( low[ x ] , low[ temp ] ) ;
                 }
                 else if ( vis[ temp ])low[ x ] = gmin ( low[ x ] , dfn[ temp ] ) ;
         }
         if ( dfn[ x ]==low[ x ] ) {//构成强连通分量
                  vis[ x ] = false ;
                  color[ x ] = ++col_num ;//染色
                  while ( stack[ top ] != x ) {//清空
                           color [stack[ top ]] = col_num ;
                           vis [ stack[ top-- ] ] = false ;
                 }
                 top -- ;
         }
}

上一道模板题:

洛谷P2661 信息传递

这道题只要记录大于2的环(强连通子图)的最小环;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=1000100;
const LL mod=100000000;
int dfn[N],low[N],sta[N],tot,head[N],cnt,ans=2e9,top;
bool vis[N];
struct Node{
	int to,nex;
}edge[N*2];
void add(int p,int q){
	edge[cnt].to=q;
	edge[cnt].nex=head[p];
	head[p]=cnt++;
}
void Tarjan(int p){
	dfn[p]=++tot,low[p]=tot;
	if(!vis[p]) vis[p]=true,sta[++top]=p;
	for(int i=head[p];~i;i=edge[i].nex){
		int q=edge[i].to;
		if(!dfn[q]){
			Tarjan(q);
			low[p]=min(low[p],low[q]);
		}
		else if(vis[q]) low[p]=min(low[p],dfn[q]);
	}
	if(dfn[p]==low[p]){
		int sum=1;
		vis[p]=false;
		while(sta[top]!=p){
			vis[sta[top]]=false;
			sum++;
			top--;
		}
		top--;
		if(sum>1) ans=min(ans,sum);
	}
}
int main(){
	memset(head,-1,sizeof(head));
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int t;
		scanf("%d",&t);
		add(i,t);
	} 
	for(int i=1;i<=n;i++){
		if(!dfn[i]) Tarjan(i);
	}
	cout<<ans<<endl;
    return 0;
}
发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/104933379