强连通分量的模版 Kosaraju+Tarjan+Garbow

  PS:在贴出代码之前,我得说明内容来源——哈尔滨工业大学出版的《图论及应用》。虽然有一些错误的地方,但是不得不说是初学者该用的书。

  从效率的角度来说,Kosaraju <Tarjan<Garbow。一般网上有前两种的代码和分析。Garbow算法是Tarjan的另一种实现,但是Garbow效率更高。

  不过从复杂度来说,三种算法的时间(空间)复杂度都是O(m +n)。

  模版的调用方式很简单,初始化,建图,调用Tarjan(n)或者Kosaraju(n)或者 Garbow(n), scc就是强连通分量的个数。

(1)Kosaraju
//Kosaraju
const int N =10010, M=50010;
struct node
{
    int to, next;
}edge[M],edge2[M]; //edge是逆图,edge2是原图
int  dfn[N], head[N], head2[N],  belg[N], num[N];
//dfn时间戳
//belg记录每个点属于哪个连通分量,num记录强连通分量点的个数
bool  vis[N];
int cnt,cnt1,scc,tot,tot1;
void dfs1(int u)
{
    vis[u]=1;
    for(int k=head2[u];k!=-1;k=edge2[k].next)
        if(!vis[edge2[k].to]) dfs1(edge2[k].to);
    dfn[++cnt1]=u;
}
void dfs2(int u)
{
    vis[u]=1;
    cnt++;
    belg[u]=scc;
    for(int k=head[u];k!=-1;k=edge[k].next)
        if(!vis[edge[k].to]) dfs2(edge[k].to);
}
void  Kosaraju(int n)
{
    memset(dfn,0,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    cnt1=scc=0;
    for(int i=1;i<=n;i++)
        if(!vis[i]) dfs1(i);
    memset(vis,0,sizeof(vis));
    for(int i=cnt1;i>0;i--)
        if(!vis[dfn[i]])
        {
            cnt=0;
            ++scc;
            dfs2(dfn[i]);
            num[scc] = cnt;
        }
}
void init()
{
    tot=tot1=0;
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(num,0,sizeof(num));
}
void addedge(int i,int j)
{
    edge2[tot1].to=j; edge2[tot1].next=head2[i];head2[i]=tot1++;
    edge[tot].to=i; edge[tot].next=head[j];head[j]=tot++;
}
(2)Tarjan
//Tarjan
const int N =1010, M=100010;
struct node
{
    int to, next;
}edge[M];
int head[N], low[N], dfn[N], sta[N], belg[N], num[N];
bool vis[N];
int scc,index,top, tot;
void tarbfs(int u)
{
    int i,j,k,v;
    low[u]=dfn[u]=++index;
    sta[top++]=u;
    vis[u]=1;
    for(i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].to;
        if(!dfn[v])
        {
            tarbfs(v);
            if(low[u]>low[v]) low[u]=low[v];
        }
        else if(vis[v]&&low[u]>dfn[v]) low[u]=dfn[v];
    }
    if(dfn[u]==low[u])
    {
        scc++;
        do
        {
            v=sta[--top];
            vis[v]=0;
            belg[v]=scc;
            num[scc]++;
        }
        while(v!=u) ;
    }
}
void Tarjan(int n)
{
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(num,0,sizeof(num));
    memset(low,0,sizeof(low));
    index=scc=top=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarbfs(i);
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int i,int j)
{
    edge[tot].to=j; edge[tot].next=head[i];head[i]=tot++;
}
(3)Garbow
//Garbow
const int N =110, M=100010;
struct node
{
    int to, next;;
}edge[M];
int stk[N],stk2[N],head[N],low[N],belg[N];
int cn,cm,tot,scc,lay;
int Garbowbfs(int cur,int lay)
{
    stk[++cn]=cur; stk2[++cm]=cur;
    low[cur]=++lay;
    for(int i=head[cur];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!low[v]) Garbowbfs(v,lay);
        else if(!belg[v])
            while(low[stk2[cm]]>low[v]) cm--;
    }
    if(stk2[cm]==cur)
    {
        cm--;
        scc++;
        do
            belg[stk[cn]]=scc;
        while(stk[cn--]!=cur) ;
    }
    return 0;
}

void Garbow(int n)
{
    scc=lay=0;
    memset(belg,0,sizeof(belg));
    memset(low,0,sizeof(low));
    for(int i=0;i<n;i++)
        if(!low[i]) Garbowbfs(i,lay);
}
void init()
{
    memset(head,-1,sizeof(-1));
}
void addedge(int i,int j)
{
    edge[tot].to=j; edge[tot].next=head[i];head[i]=tot++;
}
时间: 2024-08-29 13:26:38

强连通分量的模版 Kosaraju+Tarjan+Garbow的相关文章

POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】

Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 23445   Accepted: 9605 Description Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M &l

求有向图的强连通分量个数 之 Kosaraju算法

代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 int maps[1100][1100],nmap[1100][1100]; 6 int vis[1001]; 7 int ans=0,aaa=0,n,m,post[1100]; 8 void dfs(int); 9 void ndfs(int); 10 int main() 11 { 12 s

Tarjan算法分解强连通分量(附详细参考文章)

Tarjan算法分解强连通分量 算法思路: 算法通过dfs遍历整个连通分量,并在遍历过程中给每个点打上两个记号:一个是时间戳,即首次访问到节点i的时刻,另一个是节点u的某一个祖先被访问的最早时刻. 时间戳用DFN数组存储,最早祖先用low数组来存,每次dfs遍历到一个节点u,即让这两个记号等于当前时刻,在后面回溯或者判断的过程中在来更新low,DNF是一定的,因为第一次访问时刻一定.然后遍历u的子节点,也就是跟u相连的点v,依次看子节点的时间戳有没有打上,也就是看他有没有被访问过.\(1\).没

UESTC 901 方老师抢银行 --Tarjan求强连通分量

思路:如果出现了一个强连通分量,那么走到这个点时一定会在强连通分量里的点全部走一遍,这样才能更大.所以我们首先用Tarjan跑一遍求出所有强连通分量,然后将强连通分量缩成点(用到栈)然后就变成了一个DAG(有向无环图),然后跑一遍DFS,不断加上遍历点的权值,如果到了网吧,则更新一遍答案,因为可以出去了. 求强连通分量时,如果low[u] == dfn[u],说明形成了一个新的强连通分量,且根为u.具体求强连通分量见:http://www.cnblogs.com/whatbeg/p/377642

求图的强连通分量--tarjan算法

一:tarjan算法详解 ?思想: ? ?做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间.(也就是之后的深搜所能到达的最小开始时间)初始时dfn[i]=low[i] ? ?在DFS过程中会形成一搜索树.在搜索树上越先遍历到的节点,显然dfn的值就越小. ? ?DFS过程中,碰到哪个节点,就将哪个节点入栈.栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈. ?

CCF 高速公路 tarjan求强连通分量

问题描述 某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路. 现在,大臣们帮国王拟了一个修高速公路的计划.看了计划后,国王发现,有些城市之间可以通过高速公路直接(不经过其他城市)或间接(经过一个或多个其他城市)到达,而有的却不能.如果城市A可以通过高速公路到达城市B,而且城市B也可以通过高速公路到达城市A,则这两个城市被称为便利城市对. 国王想知道,在大臣们给他的计划中,有多少个便利城市对. 输入

强连通分量tarjan缩点——POJ2186 Popular Cows

这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定意味着v可达u.    相互可达则属于同一个强连通分量    (Strongly Connected Component, SCC) §有向图和它的转置的强连通分量相同 §所有SCC构成一个DAG(有向无环图) dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间 low[u]为u或u的

HDU ACM 1269迷宫城堡-&gt;有向强连通分量(tarjan算法实践)

分析:本题判断一个图是否是有向强连通分量,这里使用tarjan算法求解. #include<iostream> #include<vector> #include<stack> using namespace std; vector<int> map[10002]; stack<int> tarjan_stack; int low[10002]; int dfn[10002]; bool vis[10002]; int cnt,pos; void

寻找图的强连通分量:tarjan算法简单理解

1.简介tarjan是一种使用深度优先遍历(DFS)来寻找有向图强连通分量的一种算法. 2.知识准备栈.有向图.强连通分量.DFS. 3.快速理解tarjan算法的运行机制提到DFS,能想到的是通过栈来储存沿途的点,可以找到所有的环.环本身就是联通的,所以环对于强连通分量来说环已经很接近最终答案了.要把找环变成找强连通管分量还要考虑:a.在环外是不是有其他环在这个强连通分量内(极大性) (会被认为是2个环) b.一些不能构成环的点无法被考虑到,而他们本身就是强连通分量 (2不被认为是一个强连通分