首页 > 其他分享 >强连通分量 - Kosaraju Algorithm

强连通分量 - Kosaraju Algorithm

时间:2023-04-11 18:25:02浏览次数:59  
标签:... 连通 Algorithm SCC Kosaraju position 反图 分量

推荐在唯一官方原文阅读,但本文不是转载,是多平台发布。

Kosaraju算法(aka Kosaraju-Sharir算法)是一个求强连通分量的算法。其时间复杂度为\(O(n+m)\)(邻接表)或\(O(m^2)\)(邻接矩阵)。

该算法相比Tarjan算法要更简单一些。(个人观点)

本算法的基础是反图(也被称作逆图、转置图)。

基本原理

反图

参见 OI Wiki 的解释:

对于有向图 \(G\),它的 反图 (transpose graph) 指的是点集不变,每条边反向得到的图,即:若 \(G\) 的反图为 \(G'=(V,E')\),则 $ E' = {(v,u)|(u,v)\in E }$ 。

用通俗的话来讲,就是把边反过来再建一张图。

反图中强连通分量节点对于原图不变

反图有一个性质,就是若原图\(G\)中的任意两个节点\({u,v}\in V\)双向可达,则在反图\(G'\)中其依然双向可达。

论证

令图为\(G=(V,E)\),\(G\)的反图为\(G'=(V,E')\),\(u,v\)为图\(G\)中的任意两个不同节点。
不难看出\(u,v\in V\)。
分情况讨论。

  • 若\(u,v\)是双向可达的
    则对于\(u,v\in V\),存在路径\(u\to a_0\to ...\to a_x \to v\)和\(v\to b_0\to ...\to b_y \to u\)(\(a_{0...x},b_{0...y}\in V\))。
    那么在反图\(G'\)中,也一定存在路径\(v\to a_x\to ...\to a_0 \to u\) 和 \(u\to b_y\to ...\to b_0 \to v\)(\(a_{0...x},b_{0...y}\in V\))。
    因此,对于节点\(u,v\in V\),在\(G\)于\(G'\)中都是双向可达的。
  • 若\(u,v\)是单向可达的
    不妨设对于\(u,v\in V\),存在路径\(u\to a_0\to ...\to a_x \to v\),但不存在路径从\(u\)到\(v\)。
    那么在反图\(G'\)中,也一定存在路径\(v\to a_x\to ...\to a_0 \to u\),但不存在路径从\(v\)到\(u\)。

因此,可以保证反图中强连通分量节点对于原图不变。

本算法就是利用这一性质,进行两轮搜索,从而达到找到强连通分量。

算法过程

大体上,算法可以归纳到以下四个部分:

  1. 对有向图\(G\)取逆,得到\(G\)的反向图\(G'\)
  2. 利用深度优先搜索求出\(G'\)的逆后排序
  3. 对\(G\)按照上述逆后排序的序列进行深度优先搜索
  4. 同一个深度优先搜索递归子程序中访问的所有顶点都在同一个强连通分量内

示例

这是一张图。
245-kosalaju-1.jpg

我们用DFS搜一下,记一下 return 的顺序。

从1开始,每个点都要搜一下,记得打标记。

一定要从头到尾对每一个点进行搜索,否则当图不连通的话就会出现搜不完的情况。

2 4 3 1

然后将图变换成反图。

245-kosalaju-2.jpg

再用DFS,依据上面的顺序的倒序搜一下。这个时候记录进入节点的顺序。

1 4 3   # 欸,搜不动了!(っ °Д °;)っ

此时,1 4 3 3个节点是该图的第一个强连通分量。

继续搜。

2   # 欸,搜完了

你会发现,这张图的最大的强连通分量的节点数是3。

示例代码(C++)

#include <iostream>
#include <vector>
#include <memory.h>

using namespace std;

template<int MAXN> struct Kosaraju 
    typedef int node_ptr;
    typedef vector<node_ptr> vgraph[MAXN];

    // 正常图
    vgraph graph;
    // 反图
    vgraph reverse_graph;
    // 第一次DFS顺序
    vector<node_ptr> dfs_order;

    // 第一次DFS访问标记
    int visit[MAXN];
    // 每个强连通分量的点的个数
    int SCC_size[MAXN];
    // 每个点对应的SCC id
    int SCC_node[MAXN];

    // 强连通分量个数
    int SCC_count = 1;

    Kosaraju()
    {
        memset(visit, 0, sizeof visit);
    }

    void dfs_mark(node_ptr position)
    {
        // 被访问了则不再继续
        if (visit[position]) return;
        // 标记
        visit[position] = 1;
        // DFS
        for (auto i: graph[position]) dfs_mark(i);
        // 存下顺序
        dfs_order.push_back(position);
    }

    void dfs_get_SCC(node_ptr position)
    {
        // 若该点已经被编入SCC则返回
        if (SCC_node[position]) return;
        // 标记该点所对应的id
        SCC_node[position] = SCC_count;
        // 添加对应SCC id的数量
        SCC_size[SCC_count]++;
        // 搜反图
        for (auto i: reverse_graph[position]) dfs_get_SCC(i);
    }

    void add_edge(int u, int v)
    {
        graph[u].push_back(v);
        reverse_graph[v].push_back(u);
    }

    void run_kosaraju(int node_count)
    {
        // 搜出每一个点
        for (int i = 1; i <= node_count; i++) dfs_mark(i);
        // 倒序搜SCC
        for (int i = dfs_order.size() - 1; i >= 0; i--) 
            if (!SCC_node[dfs_order[i]]) 
                ++SCC_count;
                dfs_get_SCC(dfs_order[i]);
            }
        }
    }
};

欸嘿 可别直接复制啊 这和你的码风对不上吧 被教练发现那就是另一个故事了

应用

求强连通分量

你***强连通分量算法求不了强连通分量是吧

缩点

(WIP 有人看到去源站发评论区 看到就更 懒得写了)

板子题

P2863 [USACO06JAN]The Cow Prom S

这就是一个超级基本的板子题qwq
只需要加个特判,判一下对应SCC的点数是不是大于一就行了φ(゜▽゜*)♪

Refs

标签:...,连通,Algorithm,SCC,Kosaraju,position,反图,分量
From: https://www.cnblogs.com/immccn123/p/245-kosaraju.html

相关文章

  • 31、图像连通域分析
    图像的连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域,连通域分析是指在图像中寻找出彼此互相独立的连通域并将其标记出来。提取图像中不同的连通域是图像处理中较为常用的方法,例如在车牌识别、文字识别、目标检测等领域对感兴趣区域分割与识别。一般情况下,一个......
  • 测试端口连通性
    下面以Linux平台为例,讲述测试TCP和UDP端口的方法。有两个命令可以用来测试端口,一个是telnet,一个是nc,但telnet只能用于测试TCP端口,而nc即可用于测试TCP端口也可用来测试UDP端口。【telnet命令的用法】telnetIPport例如:[root@localhost]#telnet192.168.0.18120060Trying192.1......
  • 强连通分量,tarjan
    强连通:有向图两个点互相可以到达,则称为强连通,强连通分量指将图分成多个子图,每个子图的点都能相互到达,子图就称为强连通分量;constintN=1e5+5;vector<int>e[N];intdfn[N]//dfs顺序,low[N]//往前跳能到达的最小数,dex,bel[N]//表示每一个点在哪一个强连通分量中,cn......
  • 837. 连通块中点的数量
    linkcode#include<bits/stdc++.h>usingnamespacestd;constintN=100010;intfa[N],a[N];intcnt[N];intfind(intx){ if(x!=fa[x])fa[x]=find(fa[x]); returnfa[x];}voidun(intx,inty){ x=find(x); y=find(y); if(x!=y){ fa......
  • LightOJ - 1300 Odd Personality(边双连通+奇圈判定)
    题目大意:给出一张无向图,要求找出符合条件的点条件如下:从该点出发,经过一定数量的边,又回到该点,经过的边不能重复经过,且经过的边的数量为奇数解题思路:要回到原点,且不能重复经过边,只能在边双连通分量中找了接着要判断的是有多少个点,只要边双连通分量中有奇圈,那么这个连通分量中的所......
  • Algorithm参数记录
    一、vector<Point2f>vector是一个存储二维点坐标的容器,其中每个元素都是一个Point2f类型的对象。在OpenCV中,Point2f表示一个由两个单精度浮点数构成的二维点坐标。你可以使用vector来存储一些二维坐标信息,比如图像中的关键点或轮廓点等。具体用法可以参考下面的示例:#include<o......
  • cpp: sort Algorithmic
      //TenSortAlgorithms.h:此文件包含"TenSortAlgotrthms"类。十个常用排序算法C++14//2023年4月5日涂聚文GeovinDuedit.#ifndefTENSORTALGORITHMS_H#defineTENSORTALGORITHMS_H#include<vector>//#includedirective#include<string>#include&l......
  • 8064: yuyu的虚拟世界 kosaraju强连通分量
    描述 yuyu心情不太好,于是她进入了自己的虚拟世界,其中有n个小镇(1~n编号)和m条单向道,她随便选了一个点,沿着道路往前走,她发现自己可以无限的一直走下去,正好用来打发她的时间。现在她想知道,这个世界中能有几个这样的出发点,只要她选择合适的道路,总可能让她这样一直走下去。  ......
  • 0基础shell脚本ping主机网络连通性实战讲解
    本节通过一个简单脚本,使朋友们了解脚本的基本用法,及编写方法。1、先简化版,实现本机ping主机是否连通,将结果存在一个文件#!/bin/bashifping-c3${i}>/dev/null2>&1th......
  • [Algorithm] Dynamic programming - 01 - Drawing 2-d matrix
    Problem:LevenshteinDistanceWriteafunctionthattakesintwostringsandreturnstheminimumnumberofeditoperationsthatneedtobeperformedonthefir......