这个本来是暑假的时候学图连通性的时候一起学的。没整理出来,又有些遗忘。
现在在 2-sat 里用到它来缩点。先写个简略版的。
算法:
dfn[]、low[]初始为++num(排除0),
tarjin到u入栈s,ins[u] = 1;
遍历u的所有邻点v(邻接表),
对没有tarjin的点(dfn[v]=0),tarjin(v),low[u] = min(low[u], low[v])。
对tarjin过,并还在栈中的点,low[u] = min(low[u], dfn[v])。
遍历完后,
如果dfn[u] == low[u],
栈顶v出栈,直到u==v。同一时间出栈的即为一个强连通分量。
代码:
// 求 有向图 强连通分支 tarjin算法
// 类似求 无向图 割点、桥、双连通分支
#include<cstdio>
#include<cstring>
#include<iostream>
#include<stack>
using namespace std;
#define FF(x1, x2) for(int i=x1;i<x2; i++)
#define TOMIN(x1, x2) x1=x1<x2?x1:x2;
#define MAXN 100
#define MAXM 100
struct edge{int u, v;} a[MAXM];
int first[MAXN], next[MAXM];
int n, m;
void addedge(int u, int v, int e) {next[e]=first[u]; first[u]=e; a[e].u=u; a[e].v=v;}
void read_graph() {cin>>n>>m; FF(0, m){int u,v; cin>>u>>v; addedge(u, v, i);} }
int dfn[MAXN], low[MAXN];
int num;
stack<int> s; int ins[MAXN];
void tarjin(int u)
{
dfn[u] = low[u] = ++num;
s.push(u);
ins[u] = 1;
for(int e=first[u]; e!=-1; e=next[e])
{
int v=a[e].v;
if(!dfn[v])
{
tarjin(v);
TOMIN(low[u], low[v]);
}
else if(ins[v])
{
TOMIN(low[u], dfn[v]);
}
}
if(dfn[u]==low[u])
{
while(1)
{
int v=s.top(); s.pop();
ins[v] = 0;
cout<<v<<" ";
if(u == v) break;
}
cout<<endl;
}
}
int main()
{
num=0; //点赋值
memset(dfn, 0, sizeof(dfn));
memset(first, -1, sizeof(first));
memset(ins, 0, sizeof(ins));
read_graph();
FF(1, n+1)
{
if(!dfn[i])
tarjin(i);
}
}
/*
input
6 8
1 3
3 5
5 6
1 2
4 1
2 4
4 6
3 4
output
6
5
3 4 2 1
*/