强连通分量——tarjan

学自:https://blog.csdn.net/qq_34374664/article/details/77488976

强连通:在一个有向图里面,如果有两个点a和b满足a和b之间互通,则称(a,b)强连通

强连通图:一幅图中所有点之间都满足强连通,则可以称这是一幅强连通图,(无向图必是强连通图)

强连通分量:强连通图的一幅子图如果满足子图的所有点之间都是强连通,则称这幅子图为这幅强连通图的一个强连通分量

dnf[i]:保存i是第几个搜索到的,i的时间戳,每一个dnf[i]都不一样,

low[i]:保存i所在强连通分量中最小的dnf[i]

tarjan:对于每一个x,先给dnf和low赋值,然后把x压入栈并且进行标记,然后通过邻接表循环遍历所有的以x为起点的边,对于每一条边的终点,如果这个点没有访问过,对这个点进行tarjan,最后进行回溯更新,比较x节点和子节点的low,取较小的那个更新low[x],如果这个点访问过并且还在栈里面,就比较x节点的low和子节点的dnf,取较小的去更新low[x],结束循环遍历后,比较low[x]和dnf[x],如果dnf[x]和low[x]相等,表示x是x节点所在的强连通分量中的根节点,这是遍历栈输出此强连通分量中所有的节点(为确保所有的节点都遍历到,循环遍历每一个节点进行tarjan)                                                                                              

在这里对回溯的理解很重要,懂的回溯的话就不会太难

void tarjan(int x)
{
    low[x]=dnf[x]=++cnt;
    s.push(x);
    flag[x]=1;
    for(int i=first[x];i!=-1;i=next[i])
    {
        int y=lx[i].ed;
        if(!dnf[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(flag[y])
        {
            low[x]=min(low[x],dnf[y]);
        }
    }
    if(dnf[x]==low[x])
    {
        int cst;
        do
        {
            cst=s.top();
            cout<<s.top()<<' ';
            flag[s.top()]=0;
            s.pop();
        }while(cst!=x);
        cout<<endl;
    }
    return ;
}

 

该图可以表示为:

6    8

1    2

1    4

2    3

2    5

3    6

4    5

5    1

5    6

样例演示:

一:dnf[1]=low[1]=1,标记,此时flag[2]没有被标记,tarjan(2),dnf[2]=low[2]=2,压入栈,标记,然后tarjan(3),压入栈,标记,然后tarjan(6),压入栈,标记,此时dnf[6]=low[6]=4,6出度为0,循环结束,dnf[6]==low[6],输出这个强连通分量的所有的节点,输出6

二:回溯到3,以3为起点的边循环遍历完,dnf[3]==low[3],输出3

三:回溯到2,然后遍历到5,未被标记,tarjan(5),dnf[5]=low[5]=5,压入栈,标记,遍历以5位起点的所有的边,首先是1,1访问过并且还在栈里面,low[5]=min(low[5],dnf[1]),low[5]=1,然后是6,6访问过并且不再栈里面,不进行操作,循环结束,low[5]!=dnf[5],往前回溯,low[2]=min(low[5],low[2]),low[2]跟新,low[2]=1,dnf[2]!=low[2],往前回溯,low[1]=min(low[1],low[2]),low[1]还是1,

四:遍历到以1位起点的其他边,遍历到节点4,未被访问,tarjan(4),压入栈,标记,dnf[4]=low[4]=6,循环以4为起点的所有边,5访问过,low[4]=min(low[4],dnf[5]),low[4]更新,low[4]=5,dnf[4]!=low[4],返回回溯,low[4]=min(low[4],low[5]),未更新,low[4]!=dnf[4],返回,回溯,low[1]没有更新,dnf[1]==low[1],输出4,5,2,1,,此时栈为空,所有节点都访问过,结束

#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<stack>
const int inf=0x3f3f3f3f;
const int MOD=1e9+7;
#define LL long long
#define ME0(x) memset(x,0,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
#define MEF(x) memset(x,-1,sizeof(x))
using namespace std;
int next[1005],first[1005],low[1005],dnf[1005],flag[1005],ss,ee,cnt;
stack<int> s;
struct LX
{
    int st,ed;
}lx[1005];
void tarjan(int x)
{
    low[x]=dnf[x]=++cnt;
    s.push(x);
    flag[x]=1;
    for(int i=first[x];i!=-1;i=next[i])
    {
        int y=lx[i].ed;
        if(!dnf[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(flag[y])
        {
            low[x]=min(low[x],dnf[y]);
        }
    }
    if(dnf[x]==low[x])
    {
        int cst;
        do
        {
            cst=s.top();
            cout<<s.top()<<' ';
            flag[s.top()]=0;
            s.pop();
        }while(cst!=x);
        cout<<endl;
    }
    return ;
}
int main()
{
    int n,m;
    cin>>n>>m;
    MEF(first);
    for(int m1=1;m1<=m;m1++)
    {
        cin>>ss>>ee;
        lx[m1].st=ss;
        lx[m1].ed=ee;
        next[m1]=first[ss];
        first[ss]=m1;
    }
    cnt=0;
    ME0(flag);
    ME0(dnf);
    ME0(low);
    for(int n1=1;n1<=n;n1++)
    {
        if(!dnf[n1])
        {
            tarjan(n1);
        }
    }
    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值