強聯通分量算法入門

強聯通分量,聽着名字挺高大上的,一直聽別人說但是自己也不知道是什麼東西,昨天晚上,看見別人在學習這個東西,閒來無事的無就也學了學。

強連通分量在圖論問題中得到廣泛的應用,往往可以將有向圖縮點,得到一個 DAG,於是避免了原圖中可能有環造成後效性,可以在上面進行動態規劃求解。 
 

強聯通分量究竟是幹什麼用的呢?我感覺吧他就是爲了節約時間,因爲有的題圖給的會比較大,如果我們按正常的方法去寫,一般都會超時的,既然圖這麼大,我們能不能把圖給簡化一下呢,簡單的說就是縮點,把一些可以放到因爲一起的點給他們變成一個點,這樣這個圖就會小一點了,那麼什麼樣的點纔算是相同的點呢,假如一個子圖的裏面的點的集合,任意兩個點都是可達的,那麼我們就稱這爲強聯通分量,就相當於是一個環。


強連通分量分解可以通過兩次簡單的DFS3實現。第一次DFS時,選取任意頂項點作爲起點,遍歷所看清來訪過的頂點,井在回謝前給頂點標號 (pot oder,後序遍歷)o對剩餘的未訪問過的頂
點,不斷重複上述過程。
完成標號後,越接近圖的尾部(搜索樹的葉子),頂點的標號越小。第二次DFS時,先將所有邊反向,然後以標號最大的頂點爲起點進行DFS。這樣DFS所遍歷的頂點集合就構成了一個強連通分量。之後,只要還有尚未訪問的頂點,就從中選取標號最大的頂點不斷重複上述過程。

下面給出模板,

#include<bits/stdc++.h>
using namespace std;
#define met(Q,QQ) memset(Q,QQ,sizeof(Q))
const int maxn=1e4+7;
vector<int> G[maxn];//存圖
vector<int> rG[maxn];//把邊反向後的圖
vector<int> vec;//後序遍歷順序的圖
bool vis[maxn];//訪問標記
int cmp[maxn];//所屬強聯通分量的拓撲序列
int n,m;

void add_edge(int from,int to)//存圖
{
    G[from].push_back(to);
    rG[to].push_back(from);
}

void dfs(int k)     //對圖進行dfs,並訪問到的點依次存入vec中,
{
    vis[k]=true;
    int kk=G[k].size();
    for(int i=0;i<kk;i++)
    {
        int kkk=G[k][i];
        if(!vis[kkk]) dfs(kkk);
    }
    vec.push_back(k);
}

void rdfs(int k,int cnt)//構建拓撲序
{
    vis[k]=true;
    cmp[k]=cnt;
    int kk=rG[k].size();
    for(int i=0;i<kk;i++)
    {
        int kkk=rG[k][i];
        if(!vis[kkk]) rdfs(kkk,cnt);
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    int from,to;
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&from,&to);
        add_edge(from,to);
    }
    met(vis,0);
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]) dfs(i);
    }

    met(vis,0);
    int kk=vec.size();
    int cnt=0;
    for(int i=kk-1;i>=0;i--)
    {
        if(!vis[vec[i]])
        {
            cnt++;
            int qq=vec[i];
            rdfs(qq,cnt);
        }
    }


    return 0;
}

 

感覺這個模板的最終的結果都存到了cmp數組中了,也就是圖的拓撲序,遇到題的時候只要靈活的運用cmp就行了
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章