嗯,今天好不容易把鸽了好久的缩点给弄完了……感觉好像……很简单?

算法的目的,其实就是在有向图上,把一个强连通分量缩成一个点……然后我们再对此搞搞事情,\(over\)

哦对,时间复杂度很显然是\(\Theta(n)\)的,懒得\(Proof\)了。

真是简明扼要的算法啊\(233\)

比较弱智的代码是下面的:

#include <stack>
#include <cstdio>
#include <iostream>
#define min Min
#define max Max
#define MAXN 10010
#define MAXM 50010
#define to(k) E[k].to

std::stack <int> S ;
struct Edge{
    int to, next ;
}E[MAXM] ; int head[MAXN], vis[MAXN], c ;
int N, M, A, B, Ans, dfn[MAXN], low[MAXN], cnt ;

inline int Min(int a, int b) { return a & ((a - b) >> 31) | b & (~(a - b) >> 31) ; }
inline int Max(int a, int b) { return a & ((b - a) >> 31) | b & (~(b - a) >> 31) ; }
inline void _Add(int u, int v){ E[++ cnt].to = v, E[cnt].next = head[u], head[u] = cnt ;}
void Tarjan(int u){
    S.push(u), vis[u] = 1 ;
    dfn[u] = low[u] = ++ c ;
    for (int k = head[u] ; k ; k = E[k].next){
        if (vis[to(k)]) low[u] = min(low[u], low[to(k)]) ;
        else if (!dfn[to(k)]) Tarjan(to(k)), low[u] = min(low[u], low[to(k)]) ;
    }
    if (dfn[u] == low[u]) ++ Ans ;
}
int main(){
    int i ; std::cin >> N >> M ;
    for (i = 1 ; i <= M ; ++ i) scanf("%d%d", &A, &B), _Add(A, B) ;
    for (i = 1 ; i <= N ; ++ i) if (!dfn[i]) Tarjan(i) ; printf("%d", Ans) ; return 0 ;
}

十分\(zz\)的统计联通块个数……当然还有进阶版本:

\(\mathcal{Description}\)

\(Link\)

\(\mathcal{Solution}\)

其实就是让求大小非\(1\)的联通块个数……稍微弹个栈就行了\(233\)

#include <stack>
#include <cstdio>
#include <iostream>
#define min Min
#define max Max
#define MAXN 10010
#define MAXM 50010
#define to(k) E[k].to

std::stack <int> S ;
struct Edge{
    int to, next ;
}E[MAXM] ; int head[MAXN], vis[MAXN], c ;
int N, M, A, B, Ans, dfn[MAXN], low[MAXN], cnt ;

inline int Min(int a, int b) { return a & ((a - b) >> 31) | b & (~(a - b) >> 31) ; }
inline int Max(int a, int b) { return a & ((b - a) >> 31) | b & (~(b - a) >> 31) ; }
inline void _Add(int u, int v){ E[++ cnt].to = v, E[cnt].next = head[u], head[u] = cnt ;}
void Tarjan(int u){
    S.push(u), vis[u] = 1 ;
    dfn[u] = low[u] = ++ c ;
    for (int k = head[u] ; k ; k = E[k].next){
        if (vis[to(k)]) low[u] = min(low[u], low[to(k)]) ;
        else if (!dfn[to(k)]) Tarjan(to(k)), low[u] = min(low[u], low[to(k)]) ;
    }
    if (dfn[u] == low[u]){
        int t = 0 ;
        while(!S.empty()){
            int T = S.top() ;
            ++ t ; S.pop() ;
            if (T == u) break ;
        }
        Ans += (t > 1) ;
    }
}
int main(){
    int i ; std::cin >> N >> M ;
    for (i = 1 ; i <= M ; ++ i) scanf("%d%d", &A, &B), _Add(A, B) ;
    for (i = 1 ; i <= N ; ++ i) if (!dfn[i]) Tarjan(i) ; printf("%d", Ans) ; return 0 ;
}

还有更加进阶的版本:

\(\mathcal{Description}\)

\(Link\)

\(\mathcal{Solution}\)

就是缩完点之后跑\(DP\)……\[DP ~ in ~Graph= Floyd = \text{最短路} = SPFA\]这个题里,这个思路好像没问题……

那么就直接缩完点在联通块之间跑\(SPFA\)就行。

#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define max Max
#define MAX 100010
#define to(k) E[k].to

using namespace std ;
stack <int> S ;
queue <int> q ;
struct Edge{
    int to, next, v ;
}E[MAX] ; int A, B, N, M, Ans, tot, cnt, c ;
int head[MAX], dist[MAX], Edges[MAX][2], val[MAX] ;
int base[MAX], vis[MAX], clr[MAX], dfn[MAX], low[MAX] ;

inline void Tarjan(int now){
    S.push(now), vis[now] = 1,
    low[now] = dfn[now] = ++ c ;/**/
    for (int k = head[now] ; k ; k = E[k].next){
        if(vis[to(k)]) low[now] = min(low[now], dfn[to(k)]) ;
        else if (!dfn[to(k)]) Tarjan(to(k)), low[now] = min(low[now], low[to(k)]) ;
    }
    if (dfn[now] == low[now]){
        ++ tot ;
        while(!S.empty()){
            int t = S.top() ;
            clr[t] = tot, vis[t] = 0,
            val[tot] += base[t], S.pop() ;
            if (t == now) break ;
        }
    }
}
inline int Max(int a, int b){ return a & ((b - a) >> 31) | b & (~(b - a) >> 31) ; }
inline void Clear(){cnt = 0, fill(head, head + N + 3, 0) ; memset(E, 0, sizeof(E)) ;}
inline void _Add(int u, int v){ E[++ cnt].to = v, E[cnt].next = head[u], head[u] = cnt ;}
inline void SPFA(int x){
    fill(vis, vis + N + 2, 0),
    fill(dist, dist + N + 2, 0) ;
    dist[x] = val[x], vis[x] = 1, q.push(x) ;
    while (!q.empty()){
        int now = q.front() ; q.pop(), vis[now] = 0 ;
        for (int k = head[now] ; k ; k = E[k].next){
            int v = E[k].to ;
            if (dist[v] < dist[now] + val[v]) {
                dist[v] = dist[now] + val[v] ;
                if (!vis[v]) vis[v] = 1, q.push(v) ;
            }
        }
    }
    for (int i = 1 ; i <= tot ; ++ i) Ans = max(Ans, dist[i]) ;
}
int main(){
    int i ; cin >> N >> M ;
    for (i = 1 ; i <= N ; ++ i) scanf("%d", &base[i]) ;
    for (i = 1 ; i <= M ; ++ i)
        Edges[i][0] = A, Edges[i][1] = B, scanf("%d%d", &A, &B), _Add(A, B) ;
    /**/for (i = 1 ; i <= N ; ++ i) if (!dfn[i]) Tarjan(i) ; Clear() ;
    for (i = 1 ; i <= M ; ++ i) if (clr[Edges[i][0]] != clr[Edges[i][1]]) _Add(clr[Edges[i][0]], clr[Edges[i][1]]) ;/**/
    for (i = 1 ; i <= tot ; ++ i) SPFA(i) ; printf("%d\n", Ans) ; return 0 ;
}

个人觉得缩点……没啥好说的……因为比较简单嘛……

强连通分量算法·$tarjan$初探的更多相关文章

  1. Tarjan的强连通分量算法

    Tarjan算法用于寻找图G(V,E)中的所有强连通分量,其时间复杂度为O(|V|+|E|). 所谓强连通分量就是V的某个极大子集,其中任意两个结点u,v在图中都存在一条从u到v的路径. Tarjan ...

  2. 有向图的强连通分量的求解算法Tarjan

    Tarjan算法 Tarjan算法是基于dfs算法,每一个强连通分量为搜索树中的一颗子树.搜索时,把当前搜索树中的未处理的结点加入一个栈中,回溯时可以判断栈顶到栈中的结点是不是在同一个强连通分量中.当 ...

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

    有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G ...

  4. 强连通分量的Tarjan算法

    资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tar ...

  5. 有向图的强连通分量——Tarjan

    在同一个DFS树中分离不同的强连通分量SCC; 考虑一个强连通分量C,设第一个被发现的点是 x,希望在 x 访问完时立刻输出 C,这样就可以实现 在同一个DFS树中分离不同的强连通分量了. 问题就转换 ...

  6. 有向图强连通分量 Tarjan算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  7. 图的强连通分量-Kosaraju算法

    输入一个有向图,计算每个节点所在强连通分量的编号,输出强连通分量的个数 #include<iostream> #include<cstring> #include<vec ...

  8. POJ2186 Popular Cows 强连通分量tarjan

    做这题主要是为了学习一下tarjan的强连通分量,因为包括桥,双连通分量,强连通分量很多的求法其实都可以源于tarjan的这种方法,通过一个low,pre数组求出来. 题意:给你许多的A->B ...

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

    原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly con ...

随机推荐

  1. Git 学习之Git 基础(二)

    Git 基础 读完本章你就能上手使用 Git 了.本章将介绍几个最基本的,也是最常用的 Git 命令,以后绝大多数时间里用到的也就是这几个命令.读完本章,你就能初始化一个新的代码仓库,做一些适当配置: ...

  2. 洛谷P3437 [POI2006]TET-Tetris 3D(二维线段树 标记永久化)

    题意 题目链接 Sol 二维线段树空间复杂度是多少啊qwqqq 为啥这题全网空间都是\(n^2\)还有人硬要说是\(nlog^2n\)呀.. 对于这题来说,因为有修改操作,我们需要在外层线段树上也打标 ...

  3. K先生的博客

    努力,不是为了要感动谁,也不是要做给哪个人看,而是要让自己随时有能力跳出自己厌恶的圈子,并拥有选择的权利. 自己既然选择了这条路,那就要不忘初心坚定的走下去!或许坚持到最后自己会伤痕累累,但,那又怎么 ...

  4. Vue项目中引用vue-resource步骤

    直接上步骤: 1.通过命令,进入到当前项目所在目录 2.输入以下命令npm install vue-resource --save 3.安装完毕后,在main.js中导入,如下: import Vue ...

  5. mysql常用语句备忘

    1.连接本地数据库 mysql -h localhost -u root -p123 2.连接远程数据库 mysql -h 192.168.0.201 -P 3306 -u root -p123 3. ...

  6. spring boot(14)-pom.xml配置

    继承spring-boot-starter-parent 要成为一个spring boot项目,首先就必须在pom.xml中继承spring-boot-starter-parent,同时指定其版本 & ...

  7. Grunt-学习。

    Grunt 依赖 Node.js 所以在安装之前确保你安装了 Node.js.然后开始安装 Grunt 实际上,安装的并不是 Grunt,而是 Grunt-cli,也就是命令行的 Grunt,这样你就 ...

  8. 9.Java注解(Annotation)

    一.系统内置标准注解 1.@Override 是一个标记注解类型,它被用作标注方法. 它说明了被标注的方法重载了父类的方法,起到了断言的作用.如果我们使用了这种Annotation在一个没有覆盖父类方 ...

  9. [C/C++]如何解读返回函数指针的函数声明

    今天在看<深入理解C++11>的时候,看到一段有意思的代码: int (*(*pf())())() { return nullptr; } 我立刻就懵了——从来没有见过这样的函数声明.那么 ...

  10. Prometheus Node_exporter 之 CPU Memory Net Disk

    1. CPU type: GraphUnit: shortmax: "100"min: "0"Label: PercentageSystem - cpu 在内核 ...