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;
		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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章