zoukankan      html  css  js  c++  java
  • tarjan 缩点 求 scc

    算法学自 BYVoid

    https://www.byvoid.com/zhs/blog/scc-tarjan/

    这个写得很清楚了

    当然 你可能不这么认为 

    而且 如果是让我 一开始就从这个博客 学 tarjan 缩点 

    估计我也会觉得 很难懂

    我猜是 博客看多了 有了些基础 

    在看这一篇的时候懂了 

    就觉得 是这篇比较好懂

    (事实上人家本来写得就可以嘛)

    我想到了 班主任的一句话

    量的积累 才有质的变化 

    GeneralLiu

     

    tarjan 缩点 求 scc(strongly connected components)

    有向图 强连通分量

    首先 给自己 刷个广告 

    tarjan 是基于 dfs树 的算法

    我觉得 dfs树 上的一些 术语有必要知道 一下 

    所以, 看我 博客   

    还有, 就是 ,两个数组  dfn[] , low[]

    分别为     i的时间戳 ,   i能最早追溯到的时间戳

    这个比较难理解

    非常重要  

     因为 tarjan 发明的 求 割点、割边 的算法

    也要活用到 这两个数组 

    (其实不用怕 tarjan ,这不过是个帅哥 的名字 罢了)

    说说我的个人理解

    dfn [ i ] 是程序第几次 dfs 到 节点 i 

    所以起名叫 dfn ( dfs 的 第 n 次执行 ,n ∈ [ 1 , MAXN ] );

    low [ i ] 是 dfs 过程中 有时会

    遇到回到 之前 节点的 路径 ( 之前 是指先前 dfs 到 的 点 )

    那么 节点 i 就能 沿着 这条路 返回 之前的点

    low [ i ] 就是 i  {  [  能返回的  (  dfn值最小的  )   点  ]  的dfn值  }

    额 。理不理解都往下看吧  毕竟 量的积累 还是很有必要的 

    每次dfs(点u){

      dfn[u] = 进入 dfs() 函数的次数  (自己定义一个时间戳记录 如 timee)

           枚举与其相邻的点v{

              如果 没有 访问过点v {   ( 就是dfs树上的树边 )

            dfs(v);

            如果 v 能追溯 到 比“u 追溯到的最早的点” 更早的点;

            那么 u 就能 通过 v 来追溯到 那个点;

            low[u]=min(low[u],low[v]);

          }

           如果 访问过点v && v在栈中

           low[u]=min(low[u],dfn[v]); 

         }

      缩点

    }

     

    两个例题

    luogu1

    luogu2

    输出要求不同,

    笔者建议 独立体会

    下面的 代码 大同小异

    1

    #include<iostream>
    #include<stack>
    #include <cstring>
    using namespace std;
    int m,ans,bbk[205],bk,b[205],head[205],cnt,dfn[205],low[205],n;
    stack<int>zz;
    bool ru[205];
    struct aa{
        int to,next;
    }e[40005];
    void add(int x, int y)
    {
        e[cnt].to = y;
        e[cnt].next = head[x];
        head[x] = cnt++;
    }
    
    /*void add(int from,int to){
        e[++cnt]=(aa){to,head[from]};
        head[from]=cnt;
    }*/
    void dfs(int k){
        dfn[k]=low[k]= ++cnt;
        b[k]=1;
        zz.push(k);
        int j;
        for(int i=head[k];i!=-1;i=e[i].next){
            j=e[i].to;
            if(!dfn[j]){
                dfs(j);
                low[k]=min(low[k],low[j]);
            }
            else if(b[j]&&dfn[j]<low[k])low[k]=dfn[j];
        }
        if(dfn[k]==low[k]){
            bk++;
            do{
                j=zz.top();
                zz.pop();
                b[j]=0;
                bbk[j]=bk;
            }while(j!=k);
        }
    }
    int main(){
        cin>>n;
        memset(head, -1, sizeof(head));
        for(int x,i=1;i<=n;i++){
            cin>>x;
            while(x){
                add(i,x);
                cin>>x;
            }
        }
        cnt=0;
        for(int i=1;i<=n;i++)
          if(!dfn[i])
            dfs(i);
        for(int i=1;i<=n;i++)
          for(int y,j=head[i];j!=-1;j=e[j].next){
              y=e[j].to;
              if(bbk[y]!=bbk[i])ru[bbk[y]]=1;
          }
        for(int i=1;i<=bk;i++)
          if(!ru[i])
            ans++;
        cout<<ans;
        return 0;
    }

    2

    #include<iostream>
    #include<stack>
    using namespace std;
    int m,ans,bbk[10000],bk,b[10005],head[10005],cnt,dfn[10005],low[10005],n;
    stack<int>zz;
    struct aa{
        int to,next;
    }e[50002];
    void add(int from,int to){
        e[++cnt]=(aa){to,head[from]};
        head[from]=cnt;
    }
    void dfs(int k){
        dfn[k]=low[k]= ++cnt;
        b[k]=1;
        zz.push(k);
        int j;
        for(int i=head[k];i;i=e[i].next){
            j=e[i].to;
            if(!dfn[j]){
                dfs(j);
                low[k]=min(low[k],low[j]);
            }
            else if(b[j]&&dfn[j]<low[k])low[k]=dfn[j];
        }
        if(dfn[k]==low[k]){
            bk++;
            do{
                j=zz.top();
                zz.pop();
                b[j]=0;
                bbk[bk]++;
            }while(j!=k);
        }
    }
    int main(){
        cin>>n>>m;
        for(int x,y,i=1;i<=m;i++){
            cin>>x>>y;
            add(x,y);
        }
        cnt=0;
        for(int i=1;i<=n;i++)
          if(!dfn[i])
            dfs(i);
        for(int i=1;i<=bk;i++)
          if(bbk[i]>1)
            ans++;
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    Set Matrix Zeroes
    Summary Ranges
    Triangle
    MongoDB基本使用
    PHP Liunx 服务安全防范方案
    linux lvs 配置
    Linux下cacti的安装
    linux_nmon监控方法
    linux下rsync命令详细整理
    虚拟机安装CentOS以及SecureCRT设置【完美无错版】
  • 原文地址:https://www.cnblogs.com/1227xq/p/6707099.html
Copyright © 2011-2022 走看看