标签:说明 没有 一个 利用 通过 路径 深度优先 别人 必须
近期一直在刷这方面的题 因为没法学新知识 但又想写点什么 就水篇博文吧
简单来说,在一个有向图中,若所有点之间两两互相直接可达,则将这个图成为强连通分量
强连通分量可以是某个有向图中的子图
求强连通分量可以使用 Tarjan,Kosaraju 或者 Garbow 算法
个人感觉 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\)
这一部分代码实现如下:
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)\)
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 还是只为了告诉别人自己做对了
比我还水
标签:说明 没有 一个 利用 通过 路径 深度优先 别人 必须
原文地址:https://www.cnblogs.com/KnightL/p/14351657.html