求有向强连通分量 模板

模板

 

Kosaraju算法

int V; // 顶点数
vector<int> G[MAX_V]; // 图的邻接表表示
vector<int> rG[MAX_V]; // 把边反向后的图
vector<int> vs; // 后序遍历顺序的顶点列表
bool used[MAX_V]; // 访问标记
int cmp[MAX_V]; // 所属强连通分量的拓扑序322 第 4 章 登峰造极——高级篇
void add_edge(int from, int to)
{
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(int v)
{
    used[v] = true;
    for (int i = 0; i < G[v].size(); i++)
    {
        if (!used[G[v][i]]) dfs(G[v][i]);
    }
    vs.push_back(v);
}
void rdfs(int v, int k)
{
    used[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); i++)
    {
        if (!used[rG[v][i]]) rdfs(rG[v][i], k);
    }
}
int scc()
{
    memset(used, 0, sizeof(used));
    vs.clear();
    for (int v = 0; v < V; v++)
    {
        if (!used[v]) dfs(v);
    }
    memset(used, 0, sizeof(used));
    int k = 0;
    for (int i = vs.size() - 1; i >= 0; i--)
    {
        if (!used[vs[i]]) rdfs(vs[i], k++);
    }
    return k;
}
View Code

输出每个编号强连通

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define INF 99999999
#define MAX_V 10000
using namespace std;
int V;
vector<int>G[MAX_V];
vector<int>rG[MAX_V];
vector<int>vs;
vector<int>SCC[MAX_V];
bool used[MAX_V];
int cmp[MAX_V]; 
void add_edge(int from,int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(int v){
    used[v]=true;
    for(int i=0;i<G[v].size();i++){
        if(!used[G[v][i]])dfs(G[v][i]);
    }
    vs.push_back(v);
} 
void rdfs(int v,int k){
    used[v]=true;
    cmp[v]=k;
    SCC[k].push_back(v); 
    for(int i=0;i<rG[v].size();i++){
        if(!used[rG[v][i]])rdfs(rG[v][i],k);
    }
} 
int scc(){
    memset(used,0,sizeof(used));
    vs.clear();
    for(int i=1;i<V;i++){
        if(!used[i])dfs(i);
    }
    memset(used,0,sizeof(used));
    int k=0;
    for(int i=vs.size()-1;i>=0;i--){
        if(!used[vs[i]])rdfs(vs[i],k++);
    }
    return k;
}
void init(){//初始化有向图 
    V=13;
    add_edge(4,1);
    add_edge(2,3);
    add_edge(3,2);
    add_edge(4,3);
    add_edge(6,3);
    add_edge(6,4);
    add_edge(6,5);
    add_edge(5,7);
    add_edge(7,6);
    add_edge(9,7);
    add_edge(9,8);
    add_edge(8,10);
    add_edge(10,9);
    add_edge(11,8);
    add_edge(11,10);
    add_edge(12,11);    
} 
int main(){
    init();
    int ccount=scc();
    for(int i=0;i<ccount;i++){
        for(int j=0;j<SCC[i].size();j++){
            if(j==0)printf("连通分量%d:%d",i,SCC[i][j]); 
            else printf(" %d",SCC[i][j]);
        }
        printf("\n");
    }
    return 0;
}
View Code

tarjan算法

 

DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)

 

LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号

 

当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量。

 

void add_edge(int u,int v)
{
    edge[u].push_back(v);
}
void tarjan(int u)
{
    instack[u] = true;
    low[u] = DFN[u] = ++tot;
    ss.push(u); //将刚访问的节点入栈
    for(int i = 0;i < edge[u].size();i++)
    {
        int v = edge[u][i];
        if(!DFN[v]) //没有被访问过
        {//不能写成不在栈中,因为在栈中的一定是访问过的,但是不在栈中的也可能访问过,只是已经划入之前的强连通分量
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instack[v]) // 指向栈中节点的后向边
        {
            low[u] = min(low[u],DFN[v]);
        }
    }
    if(DFN[u] == low[u]) // u 为一个强连通分量的根
    {
        scc++;//强连通分量的编号
        int v;
        do
        {
            v = ss.top();
            ss.pop();
            belong[v] = scc; //标记每个节点所在的强连通分量
            num[scc]++; //每个强连通分量的节点个数
        }while(u != v);
    }
}
View Code

 

这个貌似比上面的要好一点

vector<int> G[maxn];
int Mark[maxn], Root[maxn], Stack[maxn];                                //时间戳,根(当前分量中时间戳最小的节点),栈
bool Instack[maxn];                                                     //是否在栈中标记
int Ssc[maxn];                                                          //每个节点所在的强连通分量的编号
int Index, Ssc_n, Top;                                                  //搜索时用的时间戳,强连通分量总数,栈顶指针

void Tarjan(int u)                                                      //u 当前搜索到的点
{
    Mark[u] = Root[u] = ++ Index;                                       //每找到一个点,对时间戳和根初始化
    Stack[Top ++] = u;                                                  //压栈
    Instack[u] = true;                                                  //在栈中标记

    int v;

    for(int i= 0; i< G[u].size(); i++)                                  //向下搜索
    {
        v = G[u][i];
        if(Mark[v] == 0)                                                //没到过的点
        {
            Tarjan(v);                                                  //先向下搜索
            if(Root[u] > Root[v]) Root[u] = Root[v];                    //更新根
        }
        else if(Instack[v] && Root[u] > Mark[v]) Root[u] = Mark[v];     //到过的点且点仍在栈中,试着看这个点能不能成为根
    }
/*对当前点的搜索结束*/
    if(Mark[u] == Root[u])                                              //当前点本身时根
    {
        Ssc_n ++;                                                       //更新强连通分量数

        do{                                                             //栈中比它后入栈的元素在以它为根的强连通分量中
            v = Stack[-- Top];
            Instack[v] = false;
            Ssc[v] = Ssc_n;
        }while(v != u);                                                 //直到它自己
    }
}

void SSC()
{
    memset(Mark, 0, sizeof Mark);                                       //初始化时间戳和栈内标记
    memset(Instack, false, sizeof Instack);
    Index = Ssc_n = Top = 0;                                            //初始化时间戳,强连通分量数,栈顶指针

    for(int i= 1; i<= N; i++)                                           //保证图上所有点都访问到
        if(Mark[i] == 0) Tarjan(i);
}
/***************************Tarjan算法模板***************************/
View Code

 

posted @ 2018-09-13 18:55  shuai_hui  阅读(134)  评论(0编辑  收藏  举报