开始学tarjan的时候,有关无向图的割点、桥、点双边双缩点都比较容易地理解了,唯独对有向图的缩点操作不甚明了。通过对luoguP2656_采蘑菇一题的解决,大致搞清了tarjan算法的正确性。

  首先放出有向图缩点tarjan函数的写法:

  1. void tarjan(int u) {
  2. dfn[u] = low[u] = ++timer;
  3. sta[++stp] = u, ins[u] = true;
  4. for (int i = head[u]; i; i = edge[i].nxt) {
  5. int v = edge[i].to;
  6. if (!dfn[v]) {
  7. tarjan(v);
  8. low[u] = min(low[u], low[v]);
  9. } else if (ins[v])
  10. low[u] = min(low[u], dfn[v]);
  11. }
  12. //////////////分割线////////////////
  13. if (dfn[u] == low[u]) {
  14. ++cnt;
  15. int x;
  16. do {
  17. x = sta[stp--];
  18. c[x] = cnt;
  19. ins[x] = false;

} while (x != u);

  1. }
  2. }

  问题主要出在函数的第二部分。遍历完u点的所有边后,第一,为什么将(dfn[u] == low[u])作为构成强连通分量的判定条件?第二,为什么此刻留在栈中的在u之后遍历的点能够构成一个强连通分量?

  我们先来考虑一个强连通分量的特征。当有向图中一些点构成的集合是强连通的,当且仅当这部分图满足其中任意两点u、v互通。容易联想到,具有这个特征的典型结构还有有向环;实际上,环是最简单的强连通图,而(感性上)任意一个强连通分量都可以理解成是若干个互通的简单环所构成的。这是一个很重要的想法。笔者认为把复杂的强连通分量简化为环来理解,可以较容易地说明tarjan算法的正确性。

  现在我们用两个简单的示意图来说明问题。

如上图,图一表示最简单的环情况,u是当前tarjan函数的起点。我们首先递归地将a、b、c入栈,发现三者的low值都指向了其上的u点,而不是它们自身。tarjan算法对条件(dfn[u] == low[u])的阐述是:满足该条件的u,是某个强连通分量的“根”;换言之,以u为根的搜索子树共同构成一个强连通分量。那么,我们观察这个结论的正确性何在。

1、对于该子树内的两点,若满足i点的时间戳大于j点,则i一定可以通过“前向边”(搜索边)连通至j点,这是显然的。

2、那么,为什么j点又可以通往i点呢?这就是判断条件(dfn[u] == low[u])的由来。显然,j点可以经由最后c点返回u点的那一条边,再从根u沿着前向边到达任意一个i点。

  同时,我们可以说明以u为根的原因:如果我们在脑补一条边c-->a,那么a、b、c三点也是强连通的,但是这个连通部分又与u强连通,那么这三点构成的集合便不能成为(极大)强连通分量,a不是“根”。反之,若以u为根的子树不能回溯到还在栈中的更高点而仅能到达u,则这个分量一定是完整的。

  图二为子树含两个环的情况,可以认为是更复杂的强连通分量结构。依然,对子树中任意一点v都可以返回到u,然后到达分量中的任意一点,则两个环共同与u构成强连通分量。实际上,任意的强连通结构都符合这个特征,我们总能沿着某条路径回到根,然后到达任意点,而这正是强连通分量的定义。

  最后,联想到维护栈的意义:若某些点已经被遍历过而不在栈中,则其参与构成的强连通分量必然已达到最大,不可能与栈中剩余点强连通。典型的例子是横叉边:由当前点可以回到上一个强连通分量中,而那个分量却不存在边能到达当前点,否则这个点早就在那个分量中就被前向边遍历过了。而对于子树中已经弹出的点,一定是各自构成了较小的强连通分量:因为一定存在某个子节点v,使得(dfn[v] == low[v]),即其子树不能回溯到更广的分量范围中。

  可能写得有些啰嗦,不太好明白(只有我自己知道我在说什么),所以欢迎有问题或者其他想法的同学在评论区交流。

对Tarjan——有向图缩点算法的理解的更多相关文章

  1. Reachability from the Capital(Codeforces Round #490 (Div. 3)+tarjan有向图缩点)

    题目链接:http://codeforces.com/contest/999/problem/E 题目: 题意:给你n个城市,m条单向边,问你需要加多少条边才能使得从首都s出发能到达任意一个城市. 思 ...

  2. POJ1523(割点所确定的连用分量数目,tarjan算法原理理解)

    SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7406   Accepted: 3363 Description C ...

  3. Tarjan的缩点&&割点概述

    What is Tarjan? Tarjan,是一种用来解决图的联通性的一种有效途径,它的一般俗称叫做:缩点.我们首先来设想一下: 如果我们有一个图,其中A,B,C构成一个环,那么我们在某种条件下,如 ...

  4. POJ2186(有向图缩点)

    Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 28379   Accepted: 11488 De ...

  5. POJ1904(有向图缩点+输入输出挂参考)

    King's Quest Time Limit: 15000MS   Memory Limit: 65536K Total Submissions: 8311   Accepted: 3017 Cas ...

  6. BZOJ1179 [Apio2009]Atm Tarjan 强连通缩点 动态规划

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1179 题意概括 有一个有向图,每一个节点有一个权值,其中有一些结束点. 现在,你要从S出发,到达任 ...

  7. hdu 1269 迷宫城堡 最简单的联通图题 kosaraju缩点算法

    迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Des ...

  8. tarjan算法和Kosaraju算法

    tarjan算法和Kosaraju算法是求有向图的强连通分量的算法: #include<iostream> #include<cstring> using namespace ...

  9. hdu 3072 有向图缩点成最小树形图计算最小权

    题意,从0点出发,遍历所有点,遍历边时候要付出代价,在一个SCC中的边不要付费.求最小费用. 有向图缩点(无需建立新图,,n<=50000,建则超时),遍历边,若不在一个SCC中,用一个数组更新 ...

随机推荐

  1. Azure Data Factory(一)入门简介

    一,引言 今天分享一个新的Azure 服务-----Azure Data Factory(Azure 数据工厂),怎么理解,参考根据官方解释-----数据工厂解释:大数据需要可以启用协调和操作过程以将 ...

  2. StrongArray

    * System类中包含了一个static void arraycopy(object src,int srcops,object dest ,int destpos, int length )方法, ...

  3. python使用pip安装第三方模块遇到的问题及解决方法

    python使用pip安装第三方模块遇到的问题及解决方法 关注公众号"轻松学编程"了解更多. 使用国内源: 清华:https://pypi.tuna.tsinghua.edu.cn ...

  4. 计算机二级考试:Java

    目录 第 1 章 Java 语言概论 第 2 章 基本数据类型 2.1 概述 2.1.1 标识符 2.1.2 关键字 2.1.3 常量 2.2 基本数据类型 第 3 章 运算符和表达式 3.2 算术运 ...

  5. docker搭建redis集群

    一.简介 docker作为一个容器技术,在搭建资源隔离性服务上具有很大的优势,在一台服务器上可以启动多个docker容器,感觉每个在容器里面部署的服务就像是部署在不同的服务器上.此次基于docker以 ...

  6. Spring Security 实战干货:客户端OAuth2授权请求的入口

    1. 前言 在Spring Security 实战干货:OAuth2第三方授权初体验一文中我先对OAuth2.0涉及的一些常用概念进行介绍,然后直接通过一个DEMO来让大家切身感受了OAuth2.0第 ...

  7. leetcode24:word-ladder-ii

    题目描述 给定两个单词(初始单词和目标单词)和一个单词字典,请找出所有的从初始单词到目标单词的最短转换序列: 每一次转换只能改变一个单词 每一个中间词都必须存在单词字典当中 例如: 给定的初始单词st ...

  8. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  9. 剑指Offer-Python(11-15)

    11.二进制中1的个数 链接:https://www.nowcoder.com/questionTerminal/8ee967e43c2c4ec193b040ea7fbb10b8?answerType ...

  10. Chrome默认启动尺寸的小问题

    记录一个小问题. 这是我的Chrome,他默认启动的时候是这样的: 默认启动的尺寸似乎不可调,网上没有相关资料,简直让强迫症患者无所适从,这里记录一下偶然发现的方法: 调整Chrome的尺寸,选择右上 ...