求解有向图的强联通分量--tarjan算法(tarjian求最小环模板)

强连通分量:

强连通图是指,对于图 G 中的每一对顶点 u, v,它们之间存在互相可达的两条路径:u…v 和 v …u。有向图 G 的强连通分量是指 G 的极大强连通子图。如果将每一个强连通分量缩成一个点,则原图 G 将会变成一张有向无环图(DAG)。

Tarjan算法:

任选一顶点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的顶点,则再从中任选一点再次进行)。搜索过程中已访问的顶点不再访问。搜索树的若干子树构成了图的强连通分量。

个人理解:

通过dfs算法的特性,找到某个点所能回溯到之前的哪一个点,当回溯到的某个点再也无法回溯的时候,那就从栈顶保存的点中一个一个得到相应的值,直到取到x==s.top为止(x是指low[x]==dfn[x]的点)为止我们姑且将这个点看做是强联通分量的一个根节点。从栈顶取到你之前回溯到的那个点(这两点之间的点包含这两点,即强联通分量的点集)

结合题目来整理一下tarjan算法的模板

题目链接:NOIP2015信息传递

题目思路:

借助tarjan求出强联通分量的最小的子集。

代码:

#include
using namespace std;
const int maxn=2e5+10;
vectorv[maxn];//邻接表
stacks;
int dfn[maxn],low[maxn],tot,ans=maxn,ins[maxn];
//dfn是否为0可以判断点是否访问过,ins数组用来判断点是否在栈中
//dfn数组表示顶点dfs的时间戳,low[]为u能够追溯到的最早的栈中顶点的次序号

int n,m;
void readin()
{
    int x,y;
    tot=0;
    fill(ins,ins+maxn,0);
    fill(dfn,dfn+maxn,0);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) v[i].clear();

    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        v[i].push_back(x);
    }
}
void tarjan(int x)
{
    low[x]=dfn[x]=++tot;//
    s.push(x);ins[x]=1;
    for(int i=0;i1) ans=min(ans,cnt);
    }
}

int main()
{
        readin();
        for(int i=1;i<=n;i++){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        cout<





你可能感兴趣的:(数据结构--图论)