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;
}

 

posted @ 2017-05-07 15:30  GeneralLiu  阅读(471)  评论(1编辑  收藏  举报