1. 程式人生 > >tarjan板子(割點割邊連通分量)

tarjan板子(割點割邊連通分量)

low函式實際上可以不開成陣列的(話是這麼說,但是會佔用棧空間,所以說還是老老實實寫成靜態陣列開在外面比較好)。

割點:

void tarjan_point(int i,int fd)//給邊上標號來判定重邊
{
    dfn[i]=low[i]=++dfs_clock;
    int j,id,chd=0;
    for(int p=first[i];p;p=E[p].next)
    {
        j=E[p].to,id=E[p].id;
        if(dfn[j])
        {
            if(dfn[j]<dfn[i]&&id
!=fd) low[i]=min(low[i],dfn[j]);//利用返祖邊更新low函式 continue; } chd++; tarjan_point(j,id); if(low[j]>=dfn[i]) mark[i]=1;//只要i有一個兒子滿足lowj>=dfni就說明i是割點 low[i]=min(low[i],low[j]);//利用樹邊更新low函式 } if(fd==0&&chd==1) mark[i]=0;//對於開始遍歷的根結點需要特殊判斷,可能這個點不是割點
}

割邊:

void tarjan_edge(int i,int fd)//給邊上標號來判定重邊
{
    dfn[i]=low[i]=++dfs_clock;
    int j,id;
    for(int p=first[i];p;p=E[p].next)
    {
        j=E[p].to,id=E[p].id;
        if(dfn[j])
        {
            if(dfn[j]<dfn[i]&&id!=fd) low[i]=min(low[i],dfn[j]);//利用返祖邊更新low函式 
            continue
; } tarjan_edge(j,id); low[i]=min(low[i],low[j]);//和兒子的連邊更新low函式 if(low[j]==dfn[j]) { a[++cnt]=(data){min(i,j),max(i,j)}; mark[id]=1;//改成判定這一條指向兒子的邊是不是割邊,此寫法也不受重邊的影響。 } } }

邊雙連通分量:

void tarjan_edge(int i,int fd)
{
    dfn[i]=low[i]=++dfs_clock;
    stk[++top]=i;
    int j,id;
    for(int p=first[i];p;p=E[p].next)
    {
        j=E[p].to,id=E[p].id;
        if(dfn[j])
        {
            if(dfn[j]<dfn[i]&&id!=fd) low[i]=min(low[i],dfn[j]);
            continue;
        }
        tarjan_edge(j,id);
        low[i]=min(low[i],low[j]);
        if(low[j]==dfn[j])//第一次發現割邊就應該算成一個邊雙連通分量
        {
            mark[id]=1;
            ebc_cnt++;
            while(1)
            {
                int x=stk[top--];
                ebc[ebc_cnt]++;
                ebcno[x]=ebc_cnt;
                if(x==j) break;
            }
            MAX_ebc=max(MAX_ebc,ebc[ebc_cnt]);  
        }
    }
    if(!fd&&top)//改了寫法之後需要特殊處理遍歷樹的根結點 
    {
        ebc_cnt++;
        while(top)
        {
            int x=stk[top--];
            ebc[ebc_cnt]++;
            ebcno[x]=ebc_cnt;
        }
        MAX_ebc=max(MAX_ebc,ebc[ebc_cnt]);
    }
}

點雙連通分量(割點的分量標號沒什麼意義):

void tarjan_point(int i,int fd)
{
    dfn[i]=low[i]=++dfs_clock;
    stk[++top]=i;
    int j,id,chd=0;
    for(int p=first[i];p;p=E[p].next)
    {
        j=E[p].to,id=E[p].id;
        if(dfn[j])
        {
            if(dfn[j]<dfn[i]&&fd!=id) low[i]=min(low[i],dfn[j]);
            continue;
        }
        chd++;
        tarjan_point(j,id);
        low[i]=min(low[i],low[j]);
        if(low[j]>=dfn[i])//注意判定條件和邊雙連通分量的區別 
        {
            mark[i]=1;
            bcc_cnt++;
            bcc[bcc_cnt]++;
            bccno[i]=0;
            while(1)
            {
                int x=stk[top--];
                bcc[bcc_cnt]++;
                bccno[x]=bcc_cnt;
                if(x==j) break;
            }
            MAX_bcc=max(MAX_bcc,bcc[bcc_cnt]);
        }
    }
    if(fd==0&&chd==1) mark[i]=0;
}

強連通分量:

void tarjan_scc(int i)
{
    dfn[i]=low[i]=++dfs_clock;
    stk[++top]=i;
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to;
        if(dfn[j])
        {
            if(!sccno[j]) low[i]=min(low[i],dfn[j]);//反向邊更新low函式 
            continue;
        }
        tarjan_scc(j);
        low[i]=min(low[i],low[j]);
    }
    if(low[i]==dfn[i])
    {
        scc_cnt++;
        while(1)
        {
            int x=stk[top--];
            sccno[x]=scc_cnt;
            if(x==i) break;
        }
    }
}