用途:缩点+找环
DFN:dfs序编号
low[u]:u点所能到达的,深度最小的点的DFS序编号
DFS序:DFS过程中的顺序,可记录
low【树枝边】:dfs时将要访问的边
(判断标准:该边终点尚未被访问)
【前向边】:终点已被访问过,且在子树中的边
(判断标准:终点已被访问过,且终点dfs序>起点dfs序的边)
dfn【后向边】:终点已被访问并在栈中,且不在子树中的边
【横叉边】:终点已被访问并不在栈中,且不在子树中的边
【真正需要判断并用于更新的只有树枝边(用low更新)和后向边(用dfn更新).关于前向边和横叉边……他们死了】
1 #include <iostream>
2 using namespace std;
3 const int maxn=10000;
4 int dfn[maxn];//dfs序
5 int low[maxn];//u点所能到达的,深度最小的点的DFS序编号
6 int dfscnt;//记录dfs序的计时器
7 int s[maxn],p;//栈
8 int scc[maxn];//scc[u]表示强连通分量u的编号
9 int scccnt; //表示强连通分量的数量,用作计数器
10
11 void tarjan(int u){
12 dfscnt++;
13 dfn[u]=low[u]=dfscnt;
14 s[p++]=u;
15 for(int i=head[u];i;i=e[i].next)
16 {
17 int v=e[i].to;
18 if(!dfn[v]) //若未被访问,判断树枝边,用low更新
19 {
20 tarjan(v);
21 low[u]=min(low[u],low[v]);
22 }
23 else if(!scc[v])//判断后向边,用dfn更新
24 {
25 low[u]=min(low[u],dfn[v]);
26 }
27 }
28 if(dfn[u]==low[u])
29 {
30 scccnt++; //强连通分量+1
31 scc[p]=scccnt; //令该部分的强连通编号统一
32 while(s[p]!=u)
33 {
34 p--; //该强连通分量内所有节点退栈
35 scc[p]=scccnt;
36 }
37 p--; //最后将u退栈
38 }
39 }
原理不需要理解,造轮子就完事了……
反正最后只需要用sccnum[]缩点【趴】