强连通分量Kosaraju、Tarjan【模板】

强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。

把一个图变为一个强连通图需要添加边数:先求出原图的强连通分量,缩点后变为有向无环图,计算新图入度为0的点的个数SumIn和出度为0的点的个数SumOut,答案就是max(SumIn,SumOut)。

Kosaraju算法:

1.对原图进行第一遍深度优先遍历,记录下每个节点的离开时间num[i]。

2.对原图的反向边构成的图进行第二遍深度优先遍历,从步骤(1)中离开时间最晚的点开始。第(2)步中每搜索到一棵树都是一个强连通分量Hash[]把同一连通分量上的点缩成一个点。

3.缩点之后的图就构成了DAG(有向无环图),树的个数就是强连通分量的个数。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 10010;
const int MAXM = 50050;

struct EdgeNode
{
    int to;
    int next1;
    int fr;
    int next2;
}Edges[MAXM];

int Head1[MAXN],Head2[MAXN],vis[MAXN];
int num[MAXN],Hash[MAXN],Count[MAXN],outdegree[MAXN];
int id;
//Head1[],Head2[]分别存原图和反图
//num[i]记录每个节点离开时间
//vis[i]用来记录某个点是否被访问过
//Count[sig]统计每个连通分量sig中的点个数
//Hash[cur]用来将同一个联通分量中的点cur(都属于一个sig)缩成一个点sig
void AddEdges(int u,int v)
{
    Edges[id].to = v;
    Edges[id].next1 = Head1[u];
    Head1[u] = id;
    Edges[id].fr = u;
    Edges[id].next2 = Head2[v];
    Head2[v] = id++;
}
//DFS第一遍,求出记录每个节点离开时间num[i]
void DfsOne(int cur,int& sig)
{
    vis[cur] = 1;
    for(int i = Head1[cur]; i != -1; i = Edges[i].next1)
    {
        if( !vis[Edges[i].to] )
            DfsOne(Edges[i].to,sig);
    }
    num[++sig] = cur;
}
//DFS第二遍,求出双联通分量
void DfsTwo(int cur,int sig)
{
    vis[cur] = 1;
    Hash[cur] = sig;    //Hash[]用来将同一个联通分量中的点(都所欲一个sig)缩成一个点
    Count[sig]++;       //Count[]统计每个连通分量中的点个数,sig为强连通分量个数
    for(int i = Head2[cur]; i != -1; i = Edges[i].next2)
    {
        if( !vis[Edges[i].fr])
            DfsTwo(Edges[i].fr,sig);
        else if(Hash[Edges[i].fr] != Hash[cur])     //outdegree判断缩点后新图各点是否有出度,特殊题目要求
            outdegree[Hash[Edges[i].fr]] = 1;
    }
}

int Kosaraju(int N)
{
    int sig = 0,ans;
    memset(vis,0,sizeof(vis));
    //第一次深度优先搜索
    for(int i = 1; i <= N; ++i) //DFS求得拓扑序列num[]
        if( !vis[i] )
            DfsOne(i,sig);  //sig为强连通个数

    memset(vis,0,sizeof(vis));
    memset(Count,0,sizeof(Count));
    memset(outdegree,0,sizeof(outdegree));

    //第二次深度优先搜索
    int i = sig;
    sig = 0;
    for(; i >= 1; --i)  //按照拓扑序列进行第二次dfs
        if( !vis[num[i]])
            DfsTwo(num[i],++sig);
    //算法结束,以下为特殊题目要求
    int temp = 0;
    for(int i = 1; i <= sig; i++)   //新图只有一个点出度为0才算有解,特殊题目要求
        if(!outdegree[i])
        {
            temp++;
            ans = Count[i];
        }

    //printf("$%d ",temp);
    if(temp == 1)
        return ans;
    else
        return 0;
}

int main()
{
    int N,M,u,v;
    while(~scanf("%d%d",&N,&M))
    {
        id = 0;
        memset(Head1,-1,sizeof(Head1));
        memset(Head2,-1,sizeof(Head2));
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d",&u,&v);
            AddEdges(u,v);
        }
        int ans = Kosaraju(N);
        printf("%d\n",ans);
    }

    return 0;
}

Tarjan算法:

  1. 访问一个没有被访问过的节点v;否则结束。
  2. 初始化dfn[v]和low[v]。

    对于节点v的所有邻接顶点u:

    1. 如果没有访问过,转到步骤(2),同时维护low[v]。
    2. 如果访问过,但没有删除,维护low[v]。

      如果low[v] == dfn[v],那么取出相应的强连通分量。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 10010;
const int MAXM = 50050;

struct EdgeNode
{
    int to;
    int next;
}Edges[MAXM];

int Head[MAXN],vis[MAXN],low[MAXN];
int dfn[MAXN],Stack[MAXN],outdegree[MAXN],Count[MAXN],m,id;
//dfn[v]表示顶点v被访问的时间
//low[v]为与顶点v邻接的未删除的顶点u的low[u]和low[v]的最小值
void AddEdges(int u,int v)
{
    Edges[id].to = v;
    Edges[id].next = Head[u];
    Head[u] = id++;
}

int TarBFS(int pos,int lay,int &scc)
{
    vis[pos] = 1;
    low[pos] = lay;     //初始为开始时间
    dfn[pos] = lay;     //初始为开始时间
    Stack[++m] = pos;
    //将当前未处理节点入栈,回溯时可以判断栈顶到栈中的结点是否为同一个强连通分量
    //如果当前节点是一个强连通分量的根,它的强连通分量一定是以该根为根节点的(剩下节点)子树
    for(int i = Head[pos]; i != -1; i = Edges[i].next)  //枚举每一条边
    {
        if(!vis[Edges[i].to])
            TarBFS(Edges[i].to,++lay,scc);
        if(vis[Edges[i].to] == 1)
            low[pos] = min(low[pos],low[Edges[i].to]);
    }
    //dfn[pos] == low[pos],则当前顶点就是一个强连通分量
    if(dfn[pos] == low[pos])    //缩点,low[]相同的结点属于同一个强连通分量
    {
        ++scc;//强连通分量个数
        do
        {
            Count[scc]++;       //记录每个强连通分量内的点数
            low[Stack[m]] = scc;
            vis[Stack[m]] = 2;
        }while(Stack[m--] != pos);
    }
    return 0;
}

int Tarjan(int N)
{
    int scc = 0, temp = 0, ans, lay = 1;
    m = 0;
    memset(vis,0,sizeof(vis));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    for(int i = 1; i <= N; ++i) //一次DFS求出强连通分量
        if(vis[i] == 0)
            TarBFS(i,lay,scc);  //scc得出强连通分量个数

    //下边为特殊题目要求,如果只有一个入度为0的点,则得到
    for(int i = 1; i <= N; ++i)
    {
        for(int j = Head[i]; j != -1; j = Edges[j].next)
            if(low[i] != low[Edges[j].to])
            {
                outdegree[low[i]] = 1;//标记入度不为0的点
                break;

                /*
                outdegree[low[i]]++;
                indegree[low[Edges[j].to]]++;
                //记录入度和出度不为0的点
                */
            }
    }

    for(int i = 1; i <= scc; ++i)
    {
        if(! outdegree[i])  //得到入度不为0的点个数
        {
            if(++temp > 1)
                break;
            ans = Count[i];
        }
    }

    if(temp != 1)
        return 0;
    return ans;
}
int main()
{
    int N,M,u,v;
    while(~scanf("%d%d",&N,&M))
    {
        memset(Head,-1,sizeof(Head));
        memset(outdegree,0,sizeof(outdegree));
        memset(Count,0,sizeof(Count));
        id = 0;
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d",&u,&v);
            AddEdges(u,v);
        }
        int ans = Tarjan(N);
        printf("%d\n",ans);
    }

    return 0;
}
时间: 2024-08-29 23:55:49

强连通分量Kosaraju、Tarjan【模板】的相关文章

【转载】有向图强连通分量的Tarjan算法

from byvoid [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为

有向图强连通分量的Tarjan算法(转)

原文地址:有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强

有向图强连通分量的Tarjan算法——转自BYVoid

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M).更好的

有向图强连通分量的Tarjan算法

有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,

【转】有向图强连通分量的Tarjan算法

原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量

[转]有向图强连通分量的Tarjan算法

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M).更好的

hdu1269 迷宫城堡,有向图的强连通分量 , Tarjan算法

hdu1269 迷宫城堡 验证给出的有向图是不是强连通图... Tarjan算法板子题 Tarjan算法的基础是DFS,对于每个节点.每条边都搜索一次,时间复杂度为O(V+E). 算法步骤: 1.搜索到某一个点时,将该点的Low值标上时间戳,然后将自己作为所在强连通分量的根节点(就是赋值Dfn=Low=time) 2.将该点压入栈. 3.当点p有与点p'相连时,如果此时p'不在栈中,p的low值为两点的low值中较小的一个. 4.当点p有与点p'相连时,如果此时p'在栈中,p的low值为p的lo

算法笔记_144:有向图强连通分量的Tarjan算法(Java)

目录 1 问题描述 2 解决方案 1 问题描述 引用自百度百科: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为强连通分量(strongly connected components). Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量. 定义D

强连通分量的Tarjan算法

资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tarjan算法详解理解集合 ppt图解分析下载 强连通分量 强连通分量(strongly connected component)是图论中的概念.图论中,强连通图指每一个顶点皆可以经由该图上的边抵达其他的每一个点的有向图.意即对于此图上每一个点对(Va,Vb),皆存在路径Va→Vb以及Vb→Va.强连通

POJ2186 Popular Cows 【强连通分量Kosaraju】

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