(填坑)tarjan

Tarjan算法可以用来求一个图中的强连通分量,不仅如此,还可以求图的割边、割点

接下来上一些定义

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

[点连通度与边连通度]

在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。一个图的点连通度的定义为,最小割点集合中的顶点数。

类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。一个图的边连通度的定义为,最小割边集合中的边数。

[求割点与桥]

该算法是R.Tarjan发明的。对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号。定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。根据定义,则有:

Low(u)=Min { DFS(u) DFS(v) (u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点 Low(v) (u,v)为树枝边(父子边) }

一个顶点u是割点,当且仅当满足(1)或(2) (1) u为树根,且u有多于一个子树。 (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。

一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。

以上十分专业的语言皆为转载,如果对于思路还是不大清楚的安利填坑时看的博客

https://www.byvoid.com/zhs/blog/scc-tarjan

这个是求强连通分量的

https://www.byvoid.com/zhs/blog/biconnect

这个是求割点割边还有一些其他的

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define N 3010
#define M 200010
using namespace std;
int n,m,cnt,g[N],DFN,low[M],dfn[M],cut[N],sz[N];
struct nee{
    int nxt,to;
}e[M];
inline void tarjan(int x,int f){
    low[x]=dfn[x]=++DFN;sz[x]=0;
    for(int i=g[x];i;i=e[i].nxt){
        ++sz[x];
        if(e[i].to==f) continue;
        if(dfn[e[i].to])    low[u]=min(low[u],dfn[e[i].to]);
        else{
            tarjan(e[i].to,x);
            low[x]=min(low[x],low[e[i].to]);
            if(x==1){
                if(sz[x]>=2) cut[x]=1;
            }
            else if(low[e[i].to]>dfn[x]) cut[x]=1;
        }
    }
}
inline void addedge(int x,int y){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void Jimmy(){
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    tarjan(1,0);
    for(int i=1;i<=n;i++)    
        if(cut[i]) printf("%d
",i);
} 
int main(){
    Jimmy();
    return 0;
}
tarjan求割点
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define N 3010
#define M 200010
using namespace std;
int n,m,g[M],cnt,DFN,dfn[M],low[M],cutb;
struct nee{
    int nxt,to,u,v;
}e[M];
inline addedge(int x,int y){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].u=x;e[cnt].v=y;
}
inline void tarjan(int x,int f){
    low[x]=dfn[x]=++DFN;
    for(int i=g[x];i;i=e[i].nxt){
        if(e[i].to==f) continue;
        if(dfn[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
        else {
            tarjan(e[i].to,x);
            low[x]=min(low[x],low[e[i].to]);
        }
    }
}
inline void Jimmy(){
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    tarjan(1,0);
    for(int i=1;i<=m;i++){
        if(dfn[e[i].u]>dfn[e[i].v]) swap(e[i].u,e[i].v);
        if(low[e[i].v]>dfn[e[i].u]) ++cutb;
    }
    printf("%d
",cutb);
}
int main(){
    Jimmy();
    return 0;
}
tarjan求割边

P.S.:为什么以前博客折叠代码没有标题?因为我今天才发现有这个设置的选项。。。

原文地址:https://www.cnblogs.com/JimmyC/p/6636998.html