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;
}