求解有向图的强联通分量--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<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
vector<int>v[maxn];//邻接表
stack<int>s;
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;i<v[x].size();i++){
        int p=v[x][i];
        if(!dfn[p]){//判断点是否被访问过
            tarjan(p);
            low[x]=min(low[x],low[p]);
        }
        else if(ins[p]) low[x]=min(low[x],dfn[p]);//判断点是否在栈中
    }

    if(low[x]==dfn[x]){//dfs到叶子节点,开始判断,如果==就到达了某个强联通分量的根节点,dfs回溯到了这个点
        int cnt=0;
        while(1){//得出相关的强联通分量
            int now=s.top();
            s.pop();
            ins[x]=0;//出栈visit置0
            cnt++;
            if(now==x) break;//通过栈找到那个点
        }
        if(cnt>1) ans=min(ans,cnt);
    }
}

int main()
{
        readin();
        for(int i=1;i<=n;i++){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        cout<<ans<<endl;//ans为最小环的大小
}





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值