神仙的Tarjan算法,能否让我完全理解?
Tips
- 有向图,缩强连通分量需要判断是否在栈,而无向图不需要。
求有向图的强连通分量
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
const int maxn = 1000010;
int n, m, en(1), fst[maxn], A, B;
int dfn[maxn], low[maxn], cnt, stk[maxn], top;
int bl[maxn], bcc, puted[maxn];
bool ins[maxn];
vector<int> Block[maxn];
struct Edge{
int to, nxt;
}ed[maxn];
void add(int u, int v){
ed[++en].to = v; ed[en].nxt = fst[u]; fst[u] = en;
}
void tarjan(int u){
dfn[u] = low[u] = ++cnt;
stk[++top] = u;
ins[u] = 1;
for(int e(fst[u]), v(ed[e].to); e; e = ed[e].nxt, v = ed[e].to){
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
bcc++;
do{
bl[stk[top]] = bcc;
Block[bcc].push_back(stk[top]);
ins[stk[top]] = 0;
}while(stk[top--] != u);
}
}
int main(){
n = rd(); m = rd();
for(int i(1); i <= m; ++i){
A = rd(); B = rd();
add(A, B);
}
for(int i(1); i <= n; ++i){
if(!dfn[i])tarjan(i);
}
printf("%d\n", bcc);
for(int i(1); i <= n; ++i){
if(!puted[bl[i]]){
sort(Block[bl[i]].begin(), Block[bl[i]].end());
int len = Block[bl[i]].size();
for(int j(0); j < len; ++j){
printf("%d ", Block[bl[i]][j]);
} printf("\n");
puted[bl[i]] = 1;
}
}
return 0;
}
无向图如何找出割边?
单纯找割边的话完全用不到栈。
void Tarjan(int u, int fa){
dfn[u] = low[u] = ++cnt;
for(int e(fst[u]), v(ed[e].to); e; e = ed[e].nxt, v = ed[e].to){
if((v == fa) || (v == u)) continue;
if(!dfn[v]){
Tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u]) Ans[++tot] = e;
}
else low[u] = min(low[u], dfn[v]);
}
}