图论算法之(强连通分量<Kosaraju>)

强连通分量算法有3个之多,现在介绍这种名字叫做kosaraju算法。

这个算法基于两个事实,1.原图G与逆置图GT拥有相同的强连通分量,这肯定是正确的

2.任意一个子节点存放皆后于父节点,也就是说所有只有当所有子节点都入栈了,父节点才入栈

这种在递归调用之后将顶点入队列的方式叫逆后续排序(reverse post),在无环图中这种排序方式就是拓扑排序。

简要证明:

1. 第一次DFS有向图G时,最后记录下的节点必为最后一棵生成树的根节点。 
证明:假设最后记录下节点不是树根,则必存在一节点为树根,且树根节点必为此节点祖先;而由后根序访问可知祖先节点比此节点更晚访问,矛盾;原命题成立

2. 第一次DFS的生成森林中,取两节点A、B,满足:B比A更晚记录下,且B不是A的祖先(即在第一次DFS中,A、B处于不同的生成树中);则在第二次DFS的生成森林中,B不是A的祖先,且A也不是B的祖先(即在第二次DFS中,A、B处于不同的生成树中)。 
证明:假设在第二次DFS的生成森林中,B是A的祖先,则反图GT中存在B到A路径,即第一次DFS生成森林中,A是B的祖先,则A必比B更晚记录下,矛盾;假设在第二次DFS的生成森林中,A是B的祖先,则反图GT中存在A到B路径,即第一次DFS生成森林中,B是A的祖先,矛盾;原命题成立

3. 按上述步骤求出的必为强连通分量 
证明:首先,证明2保证了第二次DFS中的每一棵树都是第一次DFS中的某棵树或某棵树的子树。其次,对于第二次DFS中的每棵树,第一次DFS保证了从根到其子孙的连通性,第二次DFS保证了根到子孙的反向连通性(即子孙到根的连通性);由此,此树中的每个节点都通过其根相互连通。

 

从以上的理解可以看出,Kosaraju算法是有局限的,比如图中强连通分量是嵌套的,Kosaraju就会出现麻烦了。

代码挺短的,但是调了一晚上

#include<cstdio>
#include<cstring>
#define N 10000+10
#define M 500000+10
using namespace std;
int head1[N],num1,head2[N],num2;
struct edge{
    int next,to;
}e1[M],e2[M];
int stack[N],vis[N],scc[N],scc_cnt;
void add1(int from,int to)
{
    e1[++num1].next=head1[from];
    e1[num1].to=to;
    head1[from]=num1;
}
void add2(int from,int to)
{
    e2[++num2].next=head2[from];
    e2[num2].to=to;
    head2[from]=num2;
}
int tot;
void Kosaraju1(int u)
{
    vis[u]=1;
    for(int i=head1[u];i;i=e1[i].next)
    {
        int v=e1[i].to;
        if(!vis[v])Kosaraju1(v);
    }
    stack[++tot]=u;
}
void Kosaraju2(int u)
{
    scc[u]=scc_cnt;
    //vis[u]=0;
    for(int i=head2[u];i;i=e2[i].next)
    {
        int v=e2[i].to;
        if(!scc[v])
        Kosaraju2(v);
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=m;i++){scanf("%d%d",&x,&y);add1(x,y);add2(y,x);}
    for(int i=1;i<=n;i++) if(!vis[i])Kosaraju1(i);
    for(int i=n;i>=1;i--){
        if(!scc[stack[i]]){scc_cnt++;Kosaraju2(stack[i]);}
    }
    printf("%d",scc_cnt);
    return 0;
时间: 2024-07-31 03:04:39

图论算法之(强连通分量<Kosaraju>)的相关文章

【Algorithms IV】求解强连通分量 Kosaraju算法

[Algorithms IV]求解强连通分量 Kosaraju算法 Kosaraju算法(也被称为Kosaraju–Sharir算法)是一个在线性时间内寻找一个有向图中的强连通分量的算法. 这个拗口的名字来自他的作者,但是查不到他的生平.应该是个印度人. 求解问题:要求有向图中的强连通分量的个数/划分 算法步骤: 即: 对输入G, 反转边获得逆向图GR     用DFS算法对图遍历得到reversePost序列(遍历图后push 到一个stack里,之后stack逆序弹出) 依次对reverse

图论算法(6)(更新版) --- Tarjan算法求强连通分量

之前Tarjan算法求强连通分量博文中,代码实现用到了固定大小数组,扩展起来似乎并不是很方便,在java里这样来实现本身就是不太妥当的,所以下面给出一个更新版本的代码实现: package test; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util

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

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

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

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

在图论中,一个有向图被成为是强连通的(strongly connected)当且仅当每一对不相同结点u和v间既存在从u到v的路径也存在从v到u的路径.有向图的极大强连通子图(这里指点数极大)被称为强连通分量(strongly connected component). 比如说这个有向图中,点\(1,2,4,5,6,7,8\)和相应边组成的子图就是一个强连通分量,另外点\(3,9\)单独构成强连通分量. Tarjan算法是由Robert Tarjan提出的用于寻找有向图的强连通分量的算法.它可以在

图论算法(6) --- Tarjan算法求强连通分量

注:此算法以有向图作为输入,并按照所在的强连通分量给出其顶点集的一个划分.graph中的每个节点只在一个强连通分量里出现,即使是单点. 任选一点开始进行深度优先搜索(若dfs结束后仍有未访问的节点,则再从中任选一点再从进行).搜索过程中已访问的节点不再访问.搜索树的若干子树构成了图的强连通分量. 节点按照被访问的顺序存入栈中.从搜索树的子树返回至一个节点时,检查该节点是否是某一连通分量的根节点,并将其从栈中删除.如果某节点是强连通分量的根,则在它之前出栈且还不属于其他强连通分量的节点构成了该节点

图的强连通分量-Kosaraju算法

输入一个有向图,计算每个节点所在强连通分量的编号,输出强连通分量的个数 1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 const int maxn=1024; 6 struct Edge{ 7 int go,next; 8 }; 9 int vis[maxn],count=0,book[maxn]; 10 vector<Edge> G,

Tarjan算法求强连通分量

一.操作过程:tarjan算法的基础是DFS.我们准备两个数组Low和Dfn.Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值(很绕嘴,往下看你就会明白),Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的.根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量. 1.数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间. 2.堆栈:每搜索到一个点,将它压入栈顶. 3.当点p有与点p’相连时,如

强连通分量-----Kosaraju

芝士: 有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为强连通分量. 如图中1,2,3,4是一个强连通分量. Kosaraju算法: 如果这是一个无向图,那么从一个节点出发,深搜得到的所有节点都是连通的. 但这是一个有向图,起始节点的不同会导致结果的不同,举个栗子,从5搜