zoukankan      html  css  js  c++  java
  • (填坑)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.:为什么以前博客折叠代码没有标题?因为我今天才发现有这个设置的选项。。。

  • 相关阅读:
    HDUOJ---Can you solve this equation?
    HDUOJ---A + B Again
    C++知识整理(进制)
    位运算的方法,大结
    位运算的方法,小结
    C++知识整理(在此感谢大牛的整理)
    nyoj---快速查找素数
    HDUOJ----湫湫系列故事——减肥记I
    《道德经》:十条简洁有用的人生建议
    Nginx的几个常用配置和技巧
  • 原文地址:https://www.cnblogs.com/JimmyC/p/6636998.html
Copyright © 2011-2022 走看看