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);
        }
    }


    
}
View Code

其中,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的逆序进行)

posted @ 2022-09-18 16:00  80k  阅读(18)  评论(0编辑  收藏  举报