给定一个有向图,定义:
- 强连通分量:在一堆点中,任意两点都可以互相到达
求所有的强联通分量
思路:
- 记录一个点的时间戳和他能到达的最早点
- 每深搜到一个点进队列,直到发现一个点能到达的最早点就是他自己,则把队尾一直到他的所有点输出,这就是一个强联通分量里的所有点
//Tanjan求强连通分量
#include<stack>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1000 + 10;
int n, m;
int lowlink[MAXN], qiang[MAXN], qiang_cnt;
int pre[MAXN], tim;
stack<int> s;
int head[MAXN], cnt;
struct edge
{
int to, next, num;
} e[MAXN];
void add(int u, int v, int y)
{
e[++cnt].next = head[u];
head[u] = cnt;
e[cnt].to = v;
e[cnt].num = y;
}
void dfs(int u)
{
pre[u] = lowlink[u] = ++tim;
s.push(u);
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]); //用后代所能到达的最早点更新u所能到达的最早点
}
else if(!qiang[v]) lowlink[u] = min(lowlink[u], pre[v]); //如果遇到一个点已经讨论过,但是还没有加入任何强连通分量,则将他的时间戳与lowlink更新
}
if(lowlink[u] == pre[u])
{
qiang_cnt++;
while(1)
{
int x = s.top(); s.pop();
qiang[x] = qiang_cnt;
if(x == u) break;
}
}
}
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
int u, v; cin >> u >> v;
add(u, v, i);
}
for(int i = 1; i <= n; i++)
if(!pre[i]) dfs(i);
for(int k = 1; k <= qiang_cnt; k++)
{
for(int i = 1; i <= n; i++)
if(qiang[i] == k) cout << i << " ";
cout << endl;
}
return 0;
}