强连通分量

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define MAXN 10010
#define MAXM 100010
struct Edge
{
    int v, next;
} edge[MAXM];   //边结点数组
int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,Belong[]为每个结点所对应的强连通分量标号数组
// Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号
int instack[10010];  // instack[]为是否在栈中的标记数组
int n, m, cnt, scnt, top, tot;
void init()
{
    cnt = 0;
    scnt = top = tot = 0;    //初始化连通分量标号,次序计数器,栈顶指针为0
    memset(first, -1, sizeof(first));
    memset(DFN, 0, sizeof(DFN));   //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用
}

void read_graph(int u, int v) //构建邻接表
{
    edge[tot].v = v;
    edge[tot].next = first[u];
    first[u] = tot++;
}

void Tarjan(int v)       //Tarjan算法求有向图的强连通分量
{
    int t;
    DFN[v] = Low[v] = ++tot;    //cnt为时间戳
    instack[v] = 1;    //标记在栈中
    stack[top++] = v;      //入栈
    for(int e = first[v]; e != -1; e = edge[e].next)
    {
        //枚举v的每一条边
        int j = edge[e].v;   //v所邻接的边
        if(!DFN[j])
        {
            //未被访问
            Tarjan(j);    //继续向下找
            if(Low[v] > Low[j])
                Low[v] = Low[j];  // 更新结点v所能到达的最小次数层
        }
        else if(instack[j] && DFN[j] < Low[v])
        {
            //如果j结点在栈内,
            Low[v] = DFN[j];
        }
    }
    if(DFN[v] == Low[v])
    {
        //如果节点v是强连通分量的根
        scnt++;   //连通分量标号加1
        do
        {
            t = stack[--top];   //退栈
            instack[t] = 0;   //标记不在栈中
            Belong[t] = scnt;   //出栈结点t属于cnt标号的强连通分量
        }
        while(t != v);   //直到将v从栈中退出
    }
}

void solve()
{
    for(int i = 1; i <= n; i++)   //枚举每个结点,搜索连通分量
        if(!DFN[i])  //未被访问
            Tarjan(i);  //则找i结点的连通分量
}

int main()
{
    while(scanf("%d%d",&n,&m) && (n || m))
    {
        init();
        while(m--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            read_graph(u, v);
        }
        solve();     //求强连通分量
        if(scnt == 1)
            printf("Yes\n");  //只有一个强连通分量,说明此图各个结点都可达
        else
            printf("No\n");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值