zoukankan      html  css  js  c++  java
  • Tarjan 算法&模板

    Tarjan 算法

    一.算法简介

    Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度。

     

    我们定义:

    如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

    例如:在上图中,{1 , 2 , 3 , 4 } , { 5 } ,  { 6 } 三个区域可以相互连通,称为这个图的强连通分量。

    Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

    再Tarjan算法中,有如下定义。

    DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)

    LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号

    当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量。

     

    二.算法图示

    以1为Tarjan 算法的起始点,如图

    顺次DFS搜到节点6

     回溯时发现LOW[ 5 ]==DFN[ 5 ] ,  LOW[ 6 ]==DFN[ 6 ] ,则{ 5 } , { 6 } 为两个强连通分量。回溯至3节点,拓展节点4.

    拓展节点1 , 发现1再栈中更新LOW[ 4 ],LOW[ 3 ] 的值为1

     回溯节点1,拓展节点2

    自此,Tarjan Algorithm 结束,{1 , 2 , 3 , 4 } , { 5 } ,  { 6 } 为图中的三个强连通分量。

    不难发现,Tarjan Algorithm 的时间复杂度为O(E+V).

    三.算法模板

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define ms(x,y) memset(x,y,sizeof(x))
    using namespace std;
    
    typedef long long ll;
    
    const double pi = acos(-1.0);
    const int mod = 1e9 + 7;
    const int maxn = 1e5 + 5;
    const int V = 150, E = 100500;
    
    struct Edge {
        int to, next;
    } edge[E];
    
    int head[V], num;   //记录树的结构
    int idx;    // 记录时间戳
    int top, S[V];  //记录堆栈
    int indeg[V], outdeg[V];    //记录入度、出度
    int low[V], dfn[V];     //low记录能追溯到最前面的点,dfn记录dfs序
    int belong[V], scc; //Belong[i] = a; 表示i这个点属于第a个连通分量 scc为连通分量个数
    bool vis[V];    //标记是否进栈
    
    int n;
    
    int addedge(int u, int v)
    {
        edge[num].to = v;
        edge[num].next = head[u];
        head[u] = num++;
    }
    
    void tarjan(int u)
    {
        int v;
        dfn[u] = low[u] = ++idx;
        S[top++] = u;
        vis[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].to;
            if (dfn[v] == 0) //v还未遍历
            {
                tarjan(v);
                low[u] = min(low[u], low[v]); //确保low[u]最小
            }
            else if (vis[v]) //如果在堆栈中
            {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) //表示找完一个连通分量
        {
            ++scc;
            do
            {
                v = S[--top];
                vis[v] = 0;
                belong[v] = scc;
            } while (u != v);
        }
    }
    
    int solve()
    {
        scc = top = idx = 0;
        ms(dfn, 0);
        ms(vis, 0);
        for (int u = 1; u <= n; u++)
        {
            if (dfn[u] == 0)
            {
                tarjan(u);
            }
        }
        return scc;
    }
    
    void count_deg()
    {
        ms(indeg, 0);
        ms(outdeg, 0);
        for (int u = 1; u <= n; u++)
        {
            for (int i = head[u]; i != -1; i = edge[i].next)
            {
                int v = edge[i].to;
                if (belong[u] != belong[v])
                {
                    indeg[belong[v]]++;     //v所在的连通块入度++
                    outdeg[belong[u]]++;    //u所在的连通块出度++
                }
            }
        }
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        while (~scanf("%d", &n))
        {
    	num=0;
            ms(head, -1);
            for (int u = 1; u <= n; u++)
            {
                int v;
                while (~scanf("%d", &v) && v)
                {
                    addedge(u, v);
                }
            }
            solve();
            if (scc == 1)
            {
                printf("1
    0
    ");
            }
            else
            {
                count_deg();
                int num_in = 0, num_out = 0;
                for (int i = 1; i <= scc; i++)
                {
                    if (indeg[i] == 0)
                        num_in++;
                    if (outdeg[i] == 0)
                        num_out++;
                }
                printf("%d
    %d
    ", num_in, max(num_in, num_out));
            }
        }
        return 0;
    }


    
    
    
    转自:http://www.cnblogs.com/shadowland/p/5872257.html
    


    
    
    
    
    
    
    
                                        
    
    Fighting~
  • 相关阅读:
    配置WebLogic的详细步骤
    安装WebLogic的详细步骤
    syswrite 函数
    利用perl 改写awk统计
    第一章 输入和输出基础
    CIO必看:跨国集团采购部报表系统的建设经验分享
    价值5000元的web报表分享
    价值5000元的web报表分享
    分页语句 where 条件列和Order by 列 引导列的问题
    Java中final修饰参数的作用
  • 原文地址:https://www.cnblogs.com/Archger/p/12774681.html
Copyright © 2011-2022 走看看