1. 程式人生 > 實用技巧 >雲裡霧裡學Tarjan(強連通分量)

雲裡霧裡學Tarjan(強連通分量)

首先讓我先來說一說關於強連通分量的一些疑惑

1、當我已經知道了我要走的下一步節點在棧中,我能不能把比較中的DFN寫成LOW?

2、如果low[v]>=dfn[u],此時u就是割點,這個東西怎麼證明?

這裡連結兩篇Tarjan演算法寫的非常好的部落格:

1、全網最!詳!細!tarjan演算法講解

2、割點(Tarjan演算法)

↑洛谷有模板

下面是鄙人寫的裸的Tarjan求圖中強連通分量的程式碼!

//Tarjan基本的演算法實現 

#include<bits/stdc++.h>
using namespace std;
struct sd{
	int v,next;
}edge[1001];
int DFN[1001],LOW[1001];
int stk[1001],head[1001],vis[1001],cnt,tot,index,n,m;
void add(int x,int y)
{//鏈式前向星 
	edge[++cnt].next=head[x];
	edge[cnt].v=y;
	head[x]=cnt;return; 
}
void tarjan(int x)
{
	DFN[x]=LOW[x]=++tot;
	stk[++index]=x;
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].next)
	{
		if(!DFN[edge[i].v])
		tarjan(edge[i].v),LOW[x]=min(LOW[x],LOW[edge[i].v]);
		else if(vis[edge[i].v])
		LOW[x]=min(LOW[x],DFN[edge[i].v]);
	}
	if(DFN[x]==LOW[x])
	{
		while(stk[index]!=x)
			printf("%d ",stk[index]),vis[stk[index]]=0,index--;
		printf("%d\n",stk[index]),vis[stk[index]]=0,index--;
	}
} 
int main()
{
	scanf("%d%d",&n,&m);
	int x,y;
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i=1;i<=n;++i)
	{
		if(!DFN[i]) tarjan(i);
	}
	return 0;
} 
/*
6 8
1 2
1 4
2 3
3 6
2 5
5 6
4 5
5 1
*/

下面是我給出的求割點的tarjan演算法程式。我還是有一些東西沒有搞懂。(帶有註釋)

這裡給一張圖方便理解:(紅色的是DFN,藍色的是LOW)

例題:P3388 【模板】割點(割頂)

//tarjan演算法求割點,我好像自己也不是很懂
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct sd{
	int next,v;
}edge[2*N];
int head[N],DFN[N],LOW[N],cnt=0,tot=0,n,m; 
bool judge[N];
void add(int x,int y)
{
	edge[++cnt].next=head[x];
	edge[cnt].v=y;
	head[x]=cnt;
}
void tarjan(int x,int fa)
{
	int child=0;
	DFN[x]=LOW[x]=++tot;
	for(int i=head[x];i;i=edge[i].next)
	{
		int v=edge[i].v;//v是要到達的點 
		if(!DFN[v])
		{
			tarjan(v,fa);
			LOW[x]=min(LOW[x],LOW[v]);
			if(LOW[v]>=DFN[x]&&x!=fa)judge[x]=true;//就是說明下一個點的最早能追溯到的節點比當前點的dfs序大或等於 
			if(x==fa)child++;//找回來了,說明已經遍歷完了一顆子樹
		}
		LOW[x]=min(LOW[x],DFN[v]);//?? 判斷回邊因為是無向圖!!!這裡不能改的過於大了不能改成LOW否則容易出問題! 
	}
	if(child>=2&&x==fa)judge[x]=true;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b);
		add(b,a);
	}
	for(int i=1;i<=n;++i)
	if(!DFN[i])tarjan(i,i);
	int ans=0;
	for(int i=1;i<=n;++i)
	if(judge[i])ans++;printf("%d\n",ans);
	for(int i=1;i<=n;++i)
	if(judge[i])printf("%d ",i);
	return 0;
}

By njc