强连通分量

定义:在一个有向图中,选取一个点集S, 如果对于S中任意两点u,v 都满足u 可以到达v ,则称S是强连通的。

如果一个强连通点集S中,不能加入更多的点使得它仍是强连通,则称S是强连通分量

(我的理解强连通分量是有向图的子集,属于它的任意两点可互通/成环)

两种算法:1 tarjan: O(n+m), 细节较多

2 Kosaraju(n+m), 细节较少,可继续优化

Kosaraju

有两次深搜,第一次用反向图深搜得到每个点的出栈序列,第二次按照q[n]...q[1]的顺序去dfs

const int MAXN = 100 ;  //最大点数 
bool vis[MAXN];
int f[MAXN],q[MAXN],t;
int G[MAXN*MAXN],NXT[MAXN*MAXN],V[MAXN*MAXN];
int G_back[MAXN*MAXN],NXT_back[MAXN*MAXN],V_back[MAXN*MAXN];

void dfs1(int x)
{
    vis[x]=1;
    for(int i=G_back[x];i;i=NXT_back[i]) if(!vis[V_back[i]]) dfs1(V_back[i]);
    q[++t]=x;
} 

void dfs2(int x,int y)
{
    vis[x]=0, f[x]=y;
    for(int i=G[x];i;i=NXT[i]) if(vis[V[i]]) dfs2(V[i],y);
}

int main()
{
    int i,n;
    for(t=0,i=1;i<=n;i++) if(!vis[i]) dfs1(i);
    for(i=n;i;i--) if(vis[q[i]]) dfs2(q[i],q[i]);
}

Tarjan

dfn时间戳,low记录所属强连通分量,先初始化这两个值

遍历能到达的点,没去过的继续深搜,搜完后更新low。如果去过,看是否在栈内,在的更新low

遍历完后看dfn和low是否相等,相等则再开一个新的强连通分量,清除栈里的属于这个强连通分量的值。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100; 
int co[MAXN]; //记录,所属强连通分量编号 
int dfn[MAXN],low[MAXN];  //记录搜索顺序,记录所属强连通
int st[MAXN];
int num,col,top; 

void Tarjan(int u)
{
    dfn[u]=low[u]=++num;
    st[++top]=u;
    for(int i=fir[u];i;i=nex[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v])  //有的版本用isinstack数组记录是否在栈内,用isinstack[v]做判断条件,这里等价 
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        co[u]=++col;
        while(st[top]!=u)
        {
            co[st[top]]=col;
            --top;
        }
        --top;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值