【筆記】tarjian演算法 求強連通分量

M_ercury_發表於2017-10-19

求解強連通分量

目的

求解強連通分量,縮環,利用節點的sccno編號重新建圖,達到題目的要求或者將原圖轉為DAG,進行圖上DPor最短路演算法

演算法流程

變數宣告:
dfn:該節點的dfs編號
low:該節點及其後代能夠追溯到的最早的祖先的dfn編號
1.到達新節點,更新dfn,將low初始化為本身的dfn序號,並將其壓入棧中
2.遍歷u的子節點v,
如果dfn[v]=0,即v還沒有被訪問過,那麼< u,v >是一條樹邊,v是u的後代,對v進行tarjian操作,根據low陣列的定義,v能到的的low,u也能到達,所以用low[v]更新low[u]
如果dfn[v]!=0&&sccno[v]!=0,說明v是u的祖先,且v不屬於之前的強連通分量,那麼用dfn[v]更新low[u]
3.如果dfn[u]==low[u]說明u節點是一個強連通分量的第一個節點,那麼將棧內元素彈出,直至彈出元素為u,目的是將不同的scc區分開來。

細節理解

dfn[v]!=0&&sccno[v]!=0,v號節點必須不屬於其他scc的原因
原理不會,但是我有例項啊
如圖,u是下面的節點,v是上面的被指向的節點。顯然v在一個scc中,u自己是一個scc
這裡寫圖片描述

此時,如果用dfn[v]更新low[u],那麼low[u]!=dfn[u],在之後的出棧操作中,u就不會被當做一個單獨的強連通,這與圖示不符。

模板

void tarjian(int u){
    dfn[u]=low[u]=++dfn_cnt;
    s[++top]=u;
    for(int i=fisrt[u];i!=-1;i=next[i]){
        int v=e[i].t;
        if(!dfn[v]){
            tarjian(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++scc_cnt;
        for(;;){
            int x=s[top];
            top--;
            sccno[x]=scc_cnt;
            if(x==u) break;
        }
    }
    return ;
}

粘一發black學長http://blog.csdn.net/loi_black的板子,black學長新開陣列in_stack記錄同一強連通分量,避免了對!sccno[v]的理解,感謝教練教我tarjian

void group(int x)
{
    dfn[x]=low[x]=++tot;    //累加遍歷序號 
    stack[++snum]=x;    //把這個點壓入棧內 
    in_stack[x]=1;      //表示這個元素在棧記憶體在 
    for(int i=first[x];i;i=next[i])
    {
        int u=hh[i].t;
        if(!dfn[u])     //這個點沒有搜過,所以這條邊是樹邊 
        {
            group(u);
            low[x]=min(low[x],low[u]);  //用low值更新low值 
        }
        else if(in_stack[u])    //搜過且在棧內,他們屬於一個強連通分量,這條邊是一條非樹邊 
            low[x]=min(low[x],dfn[u]);   //用dfn更新它的low值 
    }
    if(dfn[x]==low[x])  //x是這個強連通分量中在dfs時最先搜到的點 
    {
        cnt++;  
        while(true)
        {
            jlqlt[stack[snum]]=cnt;     //棧內的元素都是在一個強連通分量裡面 
            size[cnt]++;    //這個是記錄每個強連通分量的大小 
            in_stack[stack[snum]]=0;    //彈棧 
            snum--;
            if(stack[snum+1]==x)    //一直到彈到屬於這個強連通分量中的元素全被彈乾淨 
                break;
        }
    }
}

相關文章