强连通分量定义
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。
tarjan求强连通分量
定义以下几个变量
- dfn: 时间变量,代表给结点第几个被访问。
- Dfn[u]: 结点u的访问时间。
- Low[u]: 结点u通过有向边所能到达的最小的dfn。
- inStack[u]:结点u是否在栈中
求出每个结点所能到达的最小的Low,如果Dfn[u] == Low[u],那说明结点u经过一些边又回到了自身,那此时在结点u后面压入栈的结点就都属于一个强连通分量。
关于Low[u]的更新
- 找当前与其相连但未访问的子节点v,当递归出来时,Low[u] = min(Low[u], Low[v])。
- 找当前与其相连但已访问过的点,如果这个点在栈中,那么Low[u] = min(Low[u], Dfn[v])。
模板
hdu 1269
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
int n,m,scc,top,dfn,Low[maxn],Dfn[maxn],Stack[maxn],num[maxn];
bool inStack[maxn];
vector<int> G[maxn];
void add(int u, int v) {
G[u].push_back(v);
}
void tarjan(int u) {
Low[u] = Dfn[u] = ++dfn;
Stack[top++] = u;
inStack[u] = true;
int v;
for(int i = 0; i < G[u].size(); ++i) {
v = G[u][i];
if(!Dfn[v]) {
tarjan(v);
if(Low[u] > Low[v])
Low[u] = Low[v];
}
else if(inStack[v] && Low[u] > Dfn[v]) {
Low[u] = Dfn[v];
}
}
if(Low[u] == Dfn[u]) {
scc++;
// cout << u<<" " << scc << endl;
do {
v = Stack[--top];
inStack[v] = false;
num[scc]++;
}while(u != v);
}
}
void solve() {
top = scc = dfn = 0;
memset(Low, 0, sizeof Low);
memset(Dfn, 0, sizeof Dfn);
memset(num, 0, sizeof num);
memset(inStack, 0, sizeof inStack);
for(int i = 0; i < n; ++i) {
if(!Dfn[i]) {
tarjan(i);
}
}
}
int main() {
while(scanf("%d%d", &n, &m) &&n) {
for(int i = 0; i < n; ++i)
G[i].clear();
for(int i = 0; i < m; ++i) {
int a,b;
scanf("%d%d", &a, &b);
a--;b--;
add(a,b);
}
solve();
if(scc > 1)
puts("No");
else
puts("Yes");
}
return 0;
}