zoukankan      html  css  js  c++  java
  • tarjan算法总结

    部分内容引自https://www.cnblogs.com/stxy-ferryman/p/7779347.html

    Tarjan算法不是一个算法而是一类算法

    1.求取强连通分量

      强连通分量————有向图的强连通子图

      tarjan算法基于dfs,利用栈的思想,把下面所有的点都遍历完毕后,所能链接的最小祖先节点(可能没有),就是要寻找的强连通分量

      所以我们需要dfn数组存储dfs的遍历顺序,low数组存储这个节点后所有的子孙节点所能到达的最小节点(dfn最小)值

      为了能够得知构成这个强连通分量的所有的点,可以利用栈去记录,因为退的时候,肯定是退到最头上(如果有额外的分支,那么之前肯定早就退栈了)

      当我们遍历的时候初始化时dfn = low = idx这个初始化意思很好懂,如果没有后继节点,值就是这个

      接下来我们可能会面对三种点

      ·没有遍历过的,我们就递归tarjan遍历,然后优化low[u] = min(low[u],low[v])

      ·我们遍历过了,这个点还在栈中,那就代表这个点u可以到达,所以我们更新的时候,low[u] = min(low[u],dfn[v])_____你要知道此时的low[v]是取决于现在的low[u]的因为v在栈中,所以u是v的后继节点,这是一条返祖边

      ·我们遍历过了,这个点不在栈中了,证明这个点经历了一次退栈,形成了一次联通分量,但是不包括u,因为这个点不能到达u,如果这个点可以到达u的话,u又可以到达这个点,那么就不会退栈了(这里的到达都是要经过子孙节点的),所以对于这样的点,不必考虑任何问题

     所以直到遍历完所有的子孙节点我们就可以进行退栈的操作了,那些独立的联通分量的标志就是

      low == dfn

      意思就是对于节点u,其子孙节点所能到达的最大节点就是u,也就是形成了一个回路,环,而且可以保证这个环是最大的

    所以说了这么多,以上的算法思想用于求取一个图的强连通分量——最后进行color染色处理

    void tarjan(int u,int fa)
    {
        dfn[u] = low[u] = ++index;
        stk[s_cnt++] = u;
        instk[u] = true;
        
        for(int i = id[u];~i;i = e[i].pre)
        {
            int v = e[i].to;
            if(!dfn[v])
            {
                tarjan(v,u);
                low[u] = min(low[u],low[v]);
            }
            else if(instk[v] == true)
            {
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(dfn[u] == low[u])
        {
            col++;
            while(s_cnt > 0 && stk[s_cnt] != u)
            {
                s_cnt --;
                color[stk[s_cnt]] = col;
                instk[stk[s_cnt]] = false;
            }
        }
    }
    

     2.tarjan缩点

    利用tarjan算法可以把一个图变成单向无环图

    这个继承自强连通分量,对于每一个强连通分量,我们能够看出一个超级点,这个超级点的内部可以互相到达,然后根据这个图所表示的含义,通常要去计算超级点的度,最后输出满足题意的超级点内所有的点

    和上面的代码一致

    3·求割点和桥(割边)

    割点:去掉这个点(和这个点外射的所有边),把一个连通图变成多个连通分量

    割边:同样的道理,去掉这条边,把一个连通图变成多个连通分量

    这时候我们要判断何时是一个割点

      1.当前节点是根节点——从这个节点开始的dfs,所以如果这个点有两个子树,那么就是一个割点

      2.任意节点,如果low[v] >= dfn[u],表示u的这个子孙能够到达的最小祖先节点是比u小的,所以u是子孙v连接祖先的关键点

    void tarjan_gedian(int u,int fa)
    {
        int son = 0;
        dfn[u] = low[u] = ++index;
        for(int i = id[u];~i;i = e[i].pre)
        {
            int v = e[i].to;
            if(!dfn[v])
            {
                tarjan(v,u);
            son++; low[u] = min(low[u],low[v]); if(u != root && low[v] >= dfn[u]) { cut_point[u] = 1; } else if(u == root && son > 1) { cut_point[u] = 1; } } else if(v != fa)对于割点u这是一条回路,且u割去之后毫无影响,所以忽略这样的两点间回路情况 { low[u] = min(low[u],dfn[v]); } } }
  • 相关阅读:
    CF 142B Tprimes
    CF 231A Team
    poj 2001 Shortest Prefixes ——字典树入门
    hdu 1039 Easier Done Than Said?
    poj 2528 Mayor's posters
    hdu 1061 Rightmost Digit
    poj 2503 Babelfish
    CF271 A. Beautiful Year
    poj 2752
    CF271 B. Prime Matrix
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/9414743.html
Copyright © 2011-2022 走看看