zoukankan      html  css  js  c++  java
  • 【笔记】【图论】【强连通】

    简单的定义强连通:

    一个环,或者多个环相套,

    则一个强连通中的任何点都可以互相到达

    随手一个简单强连通,

    1->2->3->1

    思考程序如何才能让计算机模拟人的眼睛,判断出这是一个强连通,

    肯定要按照边的连通情况,遍历图,

    这里显然dfs比bfs更合适,所以我们按照1->2->3的顺序遍历

    接下来到了3->1的边,这个边肯定有特征,让计算机确定这是一个强连通

    我们发现:

    1)1号点已经遍历过

    2)这是一条回边

    这样我们可以设一个dfn的量,保存每个点遍历时的时间前后

    现在我们看我们找强连通是为了什么:

    1)计算有几个强连通

    2)把环去掉,叫做tarjan缩点

    那么对于目的1)

    我们的计数肯定是记每个强连通的结束的点,

    就是最先遍历的那个点,

    那这个点的特征(和别的点的区别)在哪呢?

    答案是他没有回边,就是他的子节点遍历中,遇不到一个点的dfn比他早

    那么我们可以找一个记录的量,先叫做t,

    这个t应该是记录每个点子节点中dfn的最小值

    (因为最高点的dfn最小,其他的都比他的dfn大,那么我们要记录的就是dfn的下限)

    那么我们就把t改成low,好听,不易重名

    思路就出来了,找没有遍历过的点遍历,

    在3节点时,找能到达的点集合,如果有一个遍历过的点,那他的dfn一定小,

    我们就更新3节点

    为了让2,这种中间点的dfn也发生改变,那么在2->3之后,也要更新2的dfn

    所以我们的结构就出来了

    但是那些遍历过的点,可能是别的强连通分量中的,所以一个stack,每次找完弹出一个强连通

    ps:

    1)tarjan中单个点也算一个强连通

    2)这里找到的是最大强连通分量,小的没管

    for(int i=1;i<=n;i++)//时间戳1:代表访问过没有 
        if(!dfn[i]) tarjan(i);
    
    bool f[N];
    stack <int> s;
    int dfn[N],low[N],ans;
    void tarjan(int x)
    {
        printf("%d %d
    ",x,tt+1); 
        dfn[x]=low[x]=++tt;
        s.push(x);f[x]=true;
        for(int i=head[x];i;i=e[i].nx )
        {
            int v=e[i].v ;
            if(!dfn[v]) 
            {
                tarjan(v);
                printf("1:%d
    ",v);
                if(low[x]>low[v]) printf("a
    ");
                low[x]=min(low[x],low[v]);
                
            }
            else if(f[v])
            {
                printf("2:%d
    ",v);
                if(low[v]>=low[x]) printf("b
    ");
                low[x]=min(low[x],low[v]);//这里用时间戳进行比较,因为时间戳大小表示先后
            
            } 
        }
        if(low[x]==dfn[x])
        {
            while(s.top() !=x) f[s.top() ]=false,s.pop() ;
            f[x]=false,s.pop() ;
            ans++;
        } 
    }

    对于目的2)

    看题!

    poj2186 Popular Cows

    告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜。

    缩点思路:将一个强连通分量->变成超级源点

    这个超级源点,用fa表示,或者用新的sum=n,++sum也行

    主要区别:

        if(dfn[u]==low[u])
        {
            color[u]=++sum;
            vis[u]=0;
            while(stack[top]!=u)
            {
                color[stack[top]]=sum;
                vis[stack[top--]]=0;
            }
            top--;
        }

    注意,缩点之后的边要区分强连通内,和强连通外,

    入度出度统计都要重新注意

  • 相关阅读:
    C++ primer plus读书笔记——第16章 string类和标准模板库
    C++ primer plus读书笔记——第15章 友元、异常和其他
    C++ primer plus读书笔记——第14章 C++中的代码重用
    C++ primer plus读书笔记——第13章 类继承
    C++ primer plus读书笔记——第12章 类和动态内存分配
    开发中常用的一些神器推荐
    收集常用的Linux常用命令
    【数据库】13种会导致索引失效语句写法
    Windows终端利器Cmder
    嵌入式操作系统的主要特点都有哪些
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11414387.html
Copyright © 2011-2022 走看看