Tarjan求强连通分量复习

给出有向图,求所有集合内点互达的集合,即强连通分量。

Algorithm 's Description

从任意节点开始DFS,形成一个树形图。除树枝边以外,还有前向边,后向边,横叉边。
前向边不影响,横叉边不能形成强连通分量,所以只需考虑后向边。
首先对于一棵树,其强连通分量一定是在树上连续的。(用反证法)
所以我们需要找到强连通分量最上面的点,其它点都是它的后代。
对于一个点,如果它的后代可以连到他的祖先,而且强连通分量的那么它一定不是最上面的点,但如果只能连到它,那么他就是强连通分量的最上面的点。
定义dfn[n]为节点n的dfs序,low[n]为节点n能连到的最上面的节点。
那么只有dfn[n] = low[n]的节点才是最上面的节点。
因此递归求出节点的low值和dfn值再判断就行了。
对于一个节点的low值,等于所有子节点的low值最小值,亦或是自己能连上去的最小dfn值,注意这里的连到的dfn一定是要在本SCC里的。

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <cstdio>
using namespace std;
const int maxn = 10005;
vector <int> lt[maxn];
stack <int> S;
int instack[maxn] , dfn[maxn] , low[maxn];
int dfs_clock = 0;
int sccno[maxn] , scccnt = 0;
void tarjan(int o){
	dfn[o] = low[o] = ++dfs_clock;
	S.push(o);
	instack[o] = 1;
	for (int i = 0; i < lt[o].size(); i++){
		int v = lt[o][i];
		if (!dfn[v]){
			tarjan(v);
			low[o] = min(low[o] , low[v]);
		}
		else if (instack[v])
		low[o] = min(low[o] , dfn[v]);
	}
	if (low[o] == dfn[o]){
		scccnt++;
		while (S.top() != o){
			int u = S.top(); S.pop();
		//	cout<<u<<" ";
			instack[u] = 0;
			sccno[u] = scccnt;
		}
		int u = S.top(); S.pop();
	//	cout<<u<<endl;
		instack[u] = 0;
		sccno[u] = scccnt;
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	for (int i = 1; i <= n; i++)
	lt[i].clear();
	for (int i = 1; i <= n; i++) dfn[i] = 0;
	for (int i = 1; i <= n; i++) low[i] = 0;
	for (int i = 1; i <= n; i++) sccno[i] = 0;
	scccnt = 0;
	dfs_clock = 0;
   	for (int i = 1; i <= m; i++){
	   	int u,v;
	   	cin>>u>>v;
	   	lt[u].push_back(v);
    }
    for (int i = 1; i <= n; i++)
    if (!dfn[i]) tarjan(i);
}

Tarjan加缩点

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <cstdio>
using namespace std;
const int maxn = 10005;
const int maxm = 500005;
vector <int> lt[maxn];
vector <int> nlt[maxn];
stack <int> S;
int instack[maxn] , dfn[maxn] , low[maxn];
int dfs_clock = 0;
int sccno[maxn] , scccnt = 0;
bool nw[maxn][maxn];
int dge[maxn];
struct edge{
	int u,v;
}e[maxm];
void tarjan(int o){
	dfn[o] = low[o] = ++dfs_clock;
	S.push(o);
	instack[o] = 1;
	for (int i = 0; i < lt[o].size(); i++){
		int v = lt[o][i];
		if (!dfn[v]){
			tarjan(v);
			low[o] = min(low[o] , low[v]);
		}
		else if (instack[v])
		low[o] = min(low[o] , dfn[v]);
	}
	if (low[o] == dfn[o]){
		scccnt++;
		while (S.top() != o){
			int u = S.top(); S.pop();
		//	cout<<u<<" ";
			instack[u] = 0;
			sccno[u] = scccnt;
		}
		int u = S.top(); S.pop();
	//	cout<<u<<endl;
		instack[u] = 0;
		sccno[u] = scccnt;
	}
}
void addedge(int u , int v){
	if (nw[u][v]) return;
	nlt[u].push_back(v);
	dge[u]++;
	nw[u][v] = 1;
}
int main(){
int t;
	int n,m;
	cin>>n>>m;
	for (int i = 1; i <= n; i++)
	lt[i].clear();
	for (int i = 1; i <= n; i++) dfn[i] = 0;
	for (int i = 1; i <= n; i++) low[i] = 0;
	for (int i = 1; i <= n; i++) sccno[i] = 0;
	for (int i = 1; i <= n; i++) dge[i] = 0;
	scccnt = 0;
	dfs_clock = 0;
   	for (int i = 1; i <= m; i++){
	   	int u,v;
	   	cin>>u>>v;
	   	e[i].u = u;
	   	e[i].v = v;
	   	lt[u].push_back(v);
    }
    for (int i = 1; i <= n; i++)
    if (!dfn[i]) tarjan(i);
    for (int i = 1; i <= m; i++)
    if (sccno[e[i].u] != sccno[e[i].v])
    addedge(sccno[e[i].u],sccno[e[i].v]);
    return 0;
}
posted @ 2017-11-10 09:11  StanChou  阅读(201)  评论(0)    收藏  举报