近期一直在刷这方面的题 因为没法学新知识 但又想写点什么 就水篇博文吧

引理

简单来说,在一个有向图中,若所有点之间两两互相直接可达,则将这个图成为强连通分量

强连通分量可以是某个有向图中的子图

求强连通分量可以使用 Tarjan,Kosaraju 或者 Garbow 算法

个人感觉 Tarjan算法 最实用,而且后两种算法并不是很熟练,因此在这里只讲 Tarjan算法

Tarjan算法

发明者 Robert E.Tarjan 罗伯特·塔扬,美国计算机科学家

塔老爷子发明过很多算法,而且大多是以他的名字命名的,所以 Tarjan算法 也分很多种,这里只说如何求强连通分量

首先需要知道什么是 DFS序

一个结点 \(x\) 的 DFS序 是指深度优先搜索遍历时改结点被搜索的次序,简记为 \(dfn[x]\)

然后,再维护另一个变量 \(low[x]\)

\(low[x]\) 表示以下节点的 DFS序 的最小值:以 \(x\) 为根的子树中的结点 和 从该子树通过一条不在搜索树上的边能到达的结点

根据 DFS 的遍历原理可以发现

  • 一个结点的子树内结点的 DFS序 都大于该结点的 DFS序

  • 从根开始的一条路径上的 DFS序 严格递增,low值 严格非降

知道了这些,再来看 Tarjan算法 求强连通分量的具体内容

我们一般只对还没有确定其 DFS序 的节点进行 Tarjan 操作,操作主要包括两个部分

第一部分

以 DFS 的形式,处理出当前点 \(x\) 的 \(dfn[x]\) 和 \(low[x]\)

对当前点打一个标记表示已经遍历过,在之后的 DFS 中根据是否遍历过来进行不同处理,具体方式如下:

设当前枚举点为 \(fr\),\(fr\) 连出去的点记为 \(to\)

  1. \(to\) 未被访问:继续对 \(to\) 进行深度搜索。在回溯过程中,用 \(low[to]\) 更新 \(low[fr]\)。因为存在从 \(fr\) 到 \(to\) 的直接路径,所以 \(to\) 能够回溯到的已经在栈中的结点, \(fr\) 也一定能够回溯到。
  2. \(to\) 被访问过,已经在栈中:即已经被访问过,根据 low值 的定义(能够回溯到的最早的已经在栈中的结点),则用 \(dfn[to]\) 更新 \(low[fr]\) 。
  3. \(to\) 被访问过,已不在在栈中:说明 \(to\) 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。

这一部分代码实现如下:

low[fr]=dfn[fr]=++cnt;vis[fr]=1;
for(int i=head[fr];i;i=e[i].nxt){
int to=e[i].to;
if(!dfn[to]) tarjan(to),low[fr]=min(low[fr],low[to]);
else if(vis[to]) low[fr]=min(low[fr],dfn[to]);
}

第二部分

对于一个连通分量图,我们很容易想到,在该连通图中有且仅有一个 \(dfn[x]=low[x]\)

该结点一定是在深度遍历的过程中,该连通分量中第一个被访问过的结点,因为它的 DFS序 和 low值 最小,不会被该连通分量中的其他结点所影响

我们可以维护一个栈,存储所有枚举到的点

因此,在回溯的过程中,判定 \(dfn[x]=low[x]\) 的条件是否成立,如果成立,则从栈中取出一个点,处理它所在的强连通分量的编号以及大小,也可以处理其他的一些操作,这样直到把所有点处理完为止

这一部分的代码实现如下:

zhan[++top]=u;
if(dfn[u]==low[u]){
++siz[++t];
int pre=zhan[top--];
vis[pre]=0;num[pre]=t;
while(pre!=u){
++siz[t];pre=zhan[top--];
vis[pre]=0;num[pre]=t;
}
}

至此,便可以处理出一个点所在的强连通分量,时间复杂度为 \(O(n+m)\)

2-SAT

SAT 是适定性(Satisfiability)问题的简称。一般形式为 k-适定性问题,简称 k-SAT。而当 \(k>2\) 时该问题为 NP 完全的。所以我们只研究 \(k=2\) 的情况。 —— OI Wiki

个人感觉,就是一个实际应用类的知识吧

就是指定 \(n\) 个集合,每个集合包含两个元素,给出若干个限制条件,每个条件规定不同集合中的某两个元素不能同时出现,最后问在这些条件下能否选出 \(n\) 个不在同一集合中的元素

这个问题一般用 Tarjan算法 来求解,也可以使用爆搜,可以参考OI Wiki上的说明,这里就只讲用 Tarjan 实现

但这种问题的实现主要不是难在 Tarjan 怎么写,而是难在图怎么建

假设这里有两个集合 \(A=\{x_1,y_1\}\),\(B=\{x_2,y_2\}\),规定 \(x_1\) 与 \(y_2\) 不可同时出现,那我们就建两条有向边 \((x_1,y_1)\),\((y_2,x_2)\),表示选了 \(x_1\) 必须选 \(y_1\),,选了 \(y_2\) 必须选 \(x_2\)

这样建完边之后只需要跑一边 Tarjan 判断有无解,若有解就把几个不矛盾的强连通分量拼起来就好了

这里注意,因为跑 Tarjan 用了栈,根据拓扑序的定义和栈的原理,可以得到 跑出来的强连通分量编号是反拓扑序 这一结论

我们就可以利用这一结论,在输出方案时倒序得到拓扑序,然后确定变量取值即可

时间复杂度同上为 \(O(n+m)\)

例题

[APIO2009]抢掠计划

[USACO5.3]校园网Network of Schools

[ZJOI2007]最大半连通子图

[POI2001]和平委员会

写在后面

这种知识加起来已经学过好几遍了,但是写起来还是很累,总是怕自己说不明白

有一说一,我是真不理解有人只放个题目链接然后放个代码是怎么想的,连思路都不提,是给别人写 std 还是只为了告诉别人自己做对了

比我还水

强连通分量 与 2-SAT的更多相关文章

  1. 强连通分量SCC 2-SAT

    强连通分量SCC 2-SAT 部分资料来自: 1.https://blog.csdn.net/whereisherofrom/article/details/79417926 2.https://ba ...

  2. HDU5934 强连通分量

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5934 根据距离关系建边 对于强连通分量来说,只需引爆话费最小的炸弹即可引爆整个强连通分量 将所有的强连通分 ...

  3. POJ1236Network of Schools[强连通分量|缩点]

    Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16571   Accepted: 65 ...

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

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

  5. Tarjan算法--强连通分量

    tarjan的过程就是dfs过程. 图一般能画成树,树的边有三种类型,树枝边 + 横叉边(两点没有父子关系) + 后向边(两点之间有父子关系): 可以看到只有后向边能构成环,即只有第三张图是强连通分量 ...

  6. 强连通分量的一二三 | | JZOJ【P1232】 | | 我也不知道我写的什么

    贴题: 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄.因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点.人间之 ...

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

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

  8. poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)

    /* 题目大意:有N个cows, M个关系 a->b 表示 a认为b popular:如果还有b->c, 那么就会有a->c 问最终有多少个cows被其他所有cows认为是popul ...

  9. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

随机推荐

  1. springMVC框架连接数据库查询数据

    1.框架搭建,创建一个maven项目打war包 web.xml文件 <?xml version="1.0" encoding="UTF-8"?> & ...

  2. Arduino IDE 开发ESP-01/ESP8266-01读取DHT11温度湿度传感器

    引脚接线: DHT11---ESP8266-01 Singnal--IO2, GND----GND, VCC----VCC DHT11引脚说明: ESP8266-01/ESP8266-01S引脚说明: ...

  3. node获取请求参数的方法get与post请求

    1.get请求 get的请求参数是携带在url中的,因此需要引入url模块对请求进行解析,再使用url.parse()方法,get请求多用于页面跳转.表单等请求中,例如page页码.表单账号密码等 先 ...

  4. Java内存模型精讲

    1.JAVA 的并发模型 共享内存模型 在共享内存的并发模型里面,线程之间共享程序的公共状态,线程之间通过读写内存中公共状态来进行隐式通信 该内存指的是主内存,实际上是物理内存的一小部分 2.JAVA ...

  5. win安装python模块出现依赖问题的解决方法 & No module named 'MySqldb'

    前言 一年多了,还在写这种问题,羞愧. 新公司不让用自己的电脑,配的winPC,项目启不起来,之前也出现过这个问题,是py3缺少某个模块,但是自己没记,这次记一下好了. No module named ...

  6. Tomcat7,Tomcat8 的manager 配置

    1.打开文件:tomcat目录-->conf-->tomcat-users.xml  2.将以下代码替换原来的所有内容,不要犹豫,就是所有内容. <?xml version=&quo ...

  7. Spring Cloud实战 | 第十篇 :Spring Cloud + Seata 1.4.1 + Nacos1.4.0 整合实现微服务架构中逃不掉的话题分布式事务

    Seata分布式事务在线体验地址:https://www.youlai.store 本篇完整源码地址:https://github.com/hxrui/youlai-mall 有想加入开源项目开发的童 ...

  8. 剑指Offer58-左转字符串

    题目 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果.对于一个给定的字符序列S,请你把其循环左移K位后的序列输出.例如,字符序列S=" ...

  9. Thread线程源码解析,Java线程的状态,线程之间的通信

    线程的基本概念 什么是线程 现代操作系统在运行一个程序的时候,会为其创建一个进程.例如,启动一个Java程序,操作系统就会创建一个Java进程.线代操作系统调度的最小单位是线程.也叫做轻量级进程.在一 ...

  10. IO软件层次结构与假脱机技术

    IO软件层次结构 用户层软件->设备独立性软件->设备驱动软件->中断处理程序->硬件 用户层软件实现与用户交互的接口,用户可直接使用该层提供的,与IO操作相关的库函数对设备进 ...