codevs2822爱在心中

本文深入讲解了Tarjan算法,一种用于图论中寻找强连通分量的经典算法。通过实例剖析,介绍了Tarjan算法的基本原理、实现过程及关键数据结构。文章提供了完整的C++代码示例,帮助读者理解算法细节。

对于tarjan算法的理解写在tarjan学习笔记(poj2186&&bzoj1051受欢迎的牛)里面了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 500000;
int low[MAXN],dfn[MAXN],nxt[MAXN],to[MAXN],tot = 1,head[MAXN],scc[MAXN],sz[MAXN];
int n,m,xbt[MAXN],rdu[MAXN],cdu[MAXN],ss[1000][1000];
bool vis[MAXN];
struct Edge
{
    int f,t;
}e[MAXN << 1];
void build(int ff,int tt)
{
    to[++tot] = tt;//我们建图只需确定该点的下一个点就行,有向边 
    nxt[tot] = head[ff];
    head[ff] = tot;
}
stack <int> s;
int tim = 0,scccnt = 0;
void dfs(int u)
{
    low[u] = dfn[u] = ++tim;
    s.push(u);
    int v ;
    for(int i = head[u] ; i ; i = nxt[i])
    {
        v = to[i];
        if(!dfn[v])//如果下一个点没有在栈中 
        {
            dfs(v);//搜到下一个点 
            low[u] = min(low[v],low[u]);//那我们的low值就是该点的low值和下一个点的low值中小的那一个 
        }
        else if(!scc[v])//如果本来就在栈中了,并且没有涂黑(就是不属于其他强连通分量) 
        {
            low[u] = min(low[u],dfn[v]);//low值的更新是取当前点的low值与搜到的点的dfn值中小的那一个 
        }       
    }
    if(low[u] == dfn[u]) 
    {
        int x ;
        scccnt ++;//数量++ 
        while(!s.empty())//出栈操作 
        {   
            x = s.top();
            s.pop();
            scc[x] = scccnt;//这些点都属于一个强连通分量 
            sz[scccnt]++;//记录这个强连通分量的个数 
            if(u == x) 
            break;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i ++)
    {
        scanf("%d%d",&e[i].f,&e[i].t);
        build(e[i].f,e[i].t);
    }
    for(int i = 1; i <= n; i ++)
    if(!dfn[i])//刚开始所有点都是白点,也就是0,在搜的过程中变灰点,出栈后涂黑 
    dfs(i);//从没有搜过的开始搜 
    int ans = 0,q=0;
    for(int i = 1; i <= m; i ++)
    {
        if(scc[e[i].f] != scc[e[i].t] )//如果这个点与它所连向的点不在一个强连通分量
        xbt[scc[e[i].f]] ++;//说明这个点所在的强连通分量不符合条件  
    }
    for(int i =1; i <= scccnt; i ++)//scccnt是强连通分量的个数 
    {
        if(sz[i] >1)
        {
            q++;//记录一下数量 
        }
    }
    int smg = 0;
    for(int i = 1; i <= scccnt; i ++)
    {
        if(!xbt[i]&&sz[i] >1)//如果这个点所在的强连通分量符合条件 是个环 
        {
            smg ++;//记录一下环的数量 
            ans = i;//这个强连通分量的编号赋给ans 
        }
    }
    cout <<q <<endl;
    if(smg > 1||ans == 0)//如果环的数量大于1 或者没有环 
    {
        puts("-1");
    }
    else//有且只有一个环 
    {
        for(int i = 1; i <= n; i ++)
        if(scc[i] == ans)//如果这个点属于这个强连通分量 
        cout <<i<<" ";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值