zoukankan      html  css  js  c++  java
  • poj1236 Tarjan算法模板 详解

    思想:

    做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间。初始时dfn[i]=low[i]

    在DFS过程中会形成一搜索树。在搜索树上越先遍历到的节点,显然dfn的值就越小。

    DFS过程中,碰到哪个节点,就将哪个节点入栈。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。

    如果发现某节点u有边连到搜索树中栈里的节点v,则更新u的low 值为dfn[v](更新为low[v]也可以)。
    如果一个节点u已经DFS访问结束,而且此时其low值等于dfn值,则说明u可达的所有节点,都不能到达任何在u之前被DFS访问的节点 ---- 那么该节点u就是一个强连通分量在DFS搜索树中的根。

    此时将栈中所有节点弹出,包括u,就找到了一个强连通分量

    以poj 1236 Network of Schools 为例说明

    192K 

       0MS
    #include <string.h>
    #include <stdio.h>
    #define V    105
    #define E    100500

    struct edge
    {
        int to, next;
    }Edge[E];
    int head[V], e, n;

    int indeg[V], outdeg[V]; //点的入度和出度数
    int belong[V], low[V], dfn[V], scc, cnt;//dfn[]:遍历到u点的时间; low[]:u点可到达的各点中最小的dfn[v]
    int S[V], top;
    bool vis[V];//v是否在栈中

    int addedge(int u, int v)
    {
        Edge[e].to = v;
        Edge[e].next = head[u];
        head[u] = e++;
        return 0;
    }
    void tarjan(int u)
    {
        int v;
        dfn[u] = low[u] = ++cnt;//开始时dfn[u] == low[u]
        S[top++] = u;//不管三七二十一进栈
        vis[u] = true;
        for (int i=head[u]; i!=-1; i=Edge[i].next)
        {
            v = Edge[i].to;
            if (dfn[v] == 0)//如果v点还未遍历
            {
                tarjan(v);//向下遍历
                low[u] = low[u] < low[v] ? low[u] : low[v];//确保low[u]最小
            }
            else if (vis[v] && low[u] > dfn[v])//v在栈中,修改low[u]
                low[u] = dfn[v];
        }
        if (dfn[u] == low[u])//u为该强连通分量中遍历所成树的根
        {
            ++scc;
            do
            {
                v = S[--top];//栈中所有到u的点都属于该强连通分量,退栈
                vis[v] = false;
                belong[v] = scc;
            } while (u != v);
        }

    }

    int solve()
    {
        scc = top = cnt = 0;
        memset(dfn, 0, sizeof(dfn));
        memset(vis, false, sizeof(vis));
        for (int u=1; u<=n; ++u)
            if (dfn[u] == 0)
                tarjan(u);
        return scc;
    }

    void count_deg()
    {
        memset(indeg, 0, sizeof(indeg));
        memset(outdeg, 0, sizeof(outdeg));
        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]]++;
                    outdeg[belong[u]]++;
                }
            }
    }

    int main()
    {
        int u, v, i;
        while (~scanf("%d", &n))
        {
            e = 0;
            memset(head, -1, sizeof(head));
            for (u=1; u<=n; ++u)
                while (scanf("%d", &v) && v != 0)
                    addedge(u, v);
            solve();
            if (scc == 1)
                printf("1 0 ");
            else
            {
                count_deg();
                int inc = 0, outc = 0;
                for (i=1; i<=scc; ++i)
                {
                    if (indeg[i] == 0)
                        inc++;
                    if (outdeg[i] == 0)
                        outc++;
                }
                printf("%d %d ", inc, (inc > outc ? inc : outc));
            }
        }
        return 0;
    }
  • 相关阅读:
    响应式布局和BootStrap 全局CSS样式
    javascript中的undefined与null的区别
    before(),after(),prepend(),append()等新DOM方法简介
    解决文字和text-decoration:underline下划线重叠问题
    CSS3 linear-gradient线性渐变实现虚线等简单实用图形
    用Javascript获取页面元素的位置
    rem、px、em(手机端h5页面屏幕适配的几种方法)
    用flex和rem实现移动端页面
    HTML5新增的form属性简介(转载至张鑫旭)
    vue实现图片放大
  • 原文地址:https://www.cnblogs.com/13224ACMer/p/4774118.html
Copyright © 2011-2022 走看看