|算法讨论|强连通分量Tarjan 学习笔记

题目
[树形DP, 缩点]BZOJ 2427:缩点后跑树上背包


模板及讲解

参考资料:https://www.byvoid.com/blog/scc-tarjan/

#include<cstdio>    
#include<cstring>    
#include<algorithm>    
#include<stack>    
#include<vector>    
#include<cmath>    
#define ms(i,j) memset(i, j, sizeof(i));    
using namespace std;    
struct node    
{    
    int x;    
    int y;    
};  //图里的边  
vector<node> q[205];  //边集  
stack<int> s;  //tarjan栈  
int n;    
int index = 0;  //时间戳  
int ans = 0;  //强连通分量个数即答案  
int ex[205];  //是否访问过,0=没访问,-1=在栈中,1=访问完毕  
int dn[205], low[205];  //dn为时间戳,low[i]为结点i能追溯到的最早栈中元素  
int tarjan(int u)    
{    
    dn[u]=low[u]=++index;  //赋值时间戳和low的初值  
    ex[u]=-1;  //标记在栈中  
    s.push(u);  //放进栈中  
    node p;    
    for (int i=0;i<q[u].size();i++)    
    {  //枚举每一条边(u,v)  
        p = q[u][i];    
        if (ex[p.y]==0) {  //结点没访问过  
            tarjan(p.y);    
            low[u] = min(low[u], low[p.y]);  //树枝,即(u,v)没访问过且u->v  
        } else if (ex[p.y]==-1)  //结点在栈中  
        {    
            low[u] = min(low[u], dn[p.y]);  //后向边,即(u,v)中的v在栈中  
        }    
    }    
    if (dn[u]==low[u])  //找到一个强连通分量  
    {    
        int e;    
        do    
        {    
            e = s.top();    
            s.pop();    
            ex[e] = 1;  //标记访问完毕  
        } while (u!=e);  //退栈直到u!=e  
        ans++;    
    }       
}    
int main ()     
{     
    scanf("%d", &n);    
    for (int i=1;i<=n;i++)    
    {    
        int a;    
        scanf("%d", &a);    
        while (a!=0)    
        {    
            node r;    
            r.x = i, r.y = a;    
            q[i].push_back(r);    
            scanf("%d", &a);    
        }    
    }    
    ms(ex, 0);    
    ms(dn,0);    
    for (int i=1;i<=n;i++)    
        if (!dn[i]) tarjan(i);    
    printf("%d\n", ans);    
    return 0;    
}    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值