AcWing 算法提高课 有向图的强连通分量
1、Tarjan算法求强连通分量:
强连通分量的点可能会向上联通。
维护两个时间戳。
模板:
#include<bits/stdc++.h> using namespace std; const int N=10010; int n,m; vector<int> adj[N]; int dfn[N],low[N]; int timestamp; bool in_stk[N]; int id[N],scc_cnt,sz[N]; int dout[N]; stack<int> stk; void tarjan(int u) { dfn[u]=low[u]=++timestamp; stk.push(u);in_stk[u]=true; for(auto nxt:adj[u]) { if(!dfn[nxt]) { tarjan(nxt); low[u]=min(low[u],low[nxt]); } else if(in_stk[nxt]) { low[u]=min(low[u],dfn[nxt]); } } if(dfn[u]==low[u]) { ++scc_cnt; int v; do { v=stk.top(); stk.pop(); in_stk[v]=false; id[v]=scc_cnt; sz[scc_cnt]++; } while(v!=u); } } int main() { cin>>n>>m; int a,b; while(m--) { cin>>a>>b; adj[a].push_back(b); } for(int i=1;i<=n;i++) { if(!dfn[i]) { tarjan(i); } } }
其中,tarjan是一个递归的算法,需要dfn和low两个时间戳。
stk和in_stk标记数组
timestamp是当前最大时间戳
id是节点属于哪个强联通分量,scc_cnt是当前的scc数量,sz数组表示当前强连通分量中点的个数。
2、一般 强连通分量可以用来缩点,将图变成一个拓扑图。
例题:
https://www.acwing.com/problem/content/1176/
3、使一个图变成强连通图至少需要加几条边
(1)先缩点,变成DAG(有向无环图)
(2)如果已经是强连通图,则答案为0,否则,设有P个起点(入度为0) Q个终点(出度为0),则答案为max(P,Q)。
4、Tarjan求半连通子图
https://www.acwing.com/problem/content/1177/
5、性质:Tarjan算法求出的强连通分量(可以缩成一点),按scc_cnt从小到大,就是拓扑序(因为按照dfs的逆序进行)