一、基本概念
强连通图(Strongly Connected Graph)是指在有向图G中,如果对于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图。
有向图中的极大强连通子图称做有向图的强连通分量。
连通分量:对于图G来的一个子图中,任意两个点都可以彼此到达,这个子图就被称为图G的连通分量(一个点就是最小的连通分量)
最大连通分量:对于图G的一个子图,这个子图为图G的连通分量,且是图G所有连通分量中包含节点数最多的那个,即为G的最大联通分量
时间戳:搜索时第几个搜索到这个点。
二、定理
定理:一个有向图G是强连通的,当且仅当G中有一个回路,它至少包含每个节点一次。
证明:
(1)充分性:如果G中有一个回路,它至少包含每个节点一次,则G中任两个节点都是互相可达的,故G是强连通图。
(2)必要性:如果有向图是强连通的,则任两个节点都是相互可达。故必可做一回路经过图中所有各点。若不然则必有一回路不包含某一结点v,并且v与回路上的个节点就不是相互可达,与强连通条件矛盾 。
三、算法
(1)Tarjan 算法
思想:
low,dfn。dfn表示这个点的时间戳,而low代表这个点所能到达的最小的时间戳,开始low都等于dfn,但会经过不断更新而减少。
从1节点进行深度优先搜索,途中用树(一个转化为栈的树)维护。
当遇到一个点时,有如下判断:
1、如果这个点没有访问过,就将这个点加入树(栈)
2、如果这个点访问过,且在树(栈)里,与这个点的low比较,更新自己的low
返回时更新low
当一个点遍历所有的边后这个点的low还是等于dfn,将个点及以上出栈,这个点及栈以上的点构成一个连通分量。
伪代码:
void tarjan(int 当前点)
{
    这个点的low=dfn=时间戳;
    将这个点入栈;
    标记这个点入栈;
    枚举这个点连接的所有边
    {
        如果目标点没有被访问过
        {
            tarjan(目标点);
            更新当前点的low; 
        }  
        如果目标点被访问过
        {
            更新当前点的low; 
        } 
    }
    如果当前点的low==dfn
    {
        将这个点及栈以上的点出栈,标记成一个强连通分量; 
        ans++; 
    } 
}  C++版本一
//in:时间戳下标
//dfn[i]:i节点的时间戳
//low[i]:i所能到达的最小的时间戳
//vis[i]:i是否在栈里
//head[i],next[i],to[i]:邻接表
void tarjan(int u)
{
    in++;
    dfn[u]=in;
    low[u]=in;
    S.push(u);
    vis[u]=1;
    for(int e=head[u];e;e=next[e])
    {
        if(!dfn[to[e]])
        {
            tarjan(to[e]);
            low[u]=min(low[to[e]],low[u]);
        }
        else if(vis[to[e]])
            low[u]=min(low[u],dfn[to[e]]);
    }
    if(low[u]==dfn[u])
    {
        while(!S.empty() && S.top()!=u)
        {
            vis[S.top()]=0;
            S.pop(); 
        } 
        vis[u]=0;
        S.pop();
        ans++;
    }
}  
(2)Korasaju 算法
四、例题
https://www.luogu.org/problemnew/show/P1726(题解:https://blog.csdn.net/weixin_43272781/article/details/89790404)

 京公网安备 11010502036488号
京公网安备 11010502036488号