强联通分量简讲(Tarjan算法)&&HDU 1269 迷宫城堡
来源:互联网 发布:爱卿网络 编辑:程序博客网 时间:2024/06/11 02:08
Problem Description
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
Input
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
Output
对于输入的每组数据,如果任意两个房间都是相互连接的,输出”Yes”,否则输出”No”。
本题是强联通分量的模板题,,主要目的是为了学一下强联通分量的基本算法;本题我用的是Tarjan算法,它的思想类似于并查集,主要的操作都是在dfs中完成的,下面我会简单讲讲我的一些理解(由于是自行脑补的成分较多,,所以有bug可以尽管提!)
注意我的实现可能和网上标准的Tarjan算法不太一样,但是用大数据的话会比网上的模板快一点点,不过不知道为什么交到HDU上会比模板慢,,可能是还有一些我没有注意到的技巧。
强联通分量的定义我就不再说了,下面主要说一说它的实现:
Tarjan的实现主体就是dfs。可以自己画一个图感受一下,有向图中,每个强联通分量都是由多个环组成的,且这些环包含了集合中的所有元素且这些环两两相交,那么如果我们对于其中的任意一个元素进行dfs的话,最终这个强连通分量里的元素就会形成一颗子树,并且如果我们把我们的起始元素看作一棵树的根节点的话,那么它的子树中(也就是这个强联通分量中)一定会有节点它的父节点方向连边,并且这些向回连的边最终都会通到根节点,并且叶节点必然会向回连边(这是很显然的)。这样才能满足这个子树中的每个节点都能到达其他的各个节点。
知道了这些性质后,我们就可以进行操作了:
我们将每个强联通分量视为一个集合,一开始所有节点都是各自独立的集合,开两个数组dfn[ ]和low[ ],dfn记录的是节点被访问的顺序,low[i]记录i节点所在集合的代表元素的dfn值(这里类似于并查集),显然对于一个新的节点,集合的代表元素就是它本身,dfn[i]=low[i],。然后我们对其进行dfs,首先将它加入一个栈内,然后对它的每一个可以到达的节点进行dfs,如果它连接的节点是它的一个父节点(或祖父节点,也就是刚刚dfs路径上的点),那么low[i] = min( low[i], low[to] );如果它连接的节点已经被访问过(也就是已经dfs过)并且不是该节点的父节点或祖父节点,直接跳过(因为既然那个节点已经dfs过,而当前节点又是本次才访问到,那么说明那个节点出发一定不能到达当前节点,也就是说它们一定不在一个强联通分量里);我们看看上面这部分操作,它和并查集的操作是很像的,我们相当于将dfs路径上的环合并成了一个集合(相交环也被合成了一个集合),并且代表元素是最先访问的那个点,而由定义可知所有相交环都是一个强联通图,那么我们在回溯时,判断该节点的low[ ]是否等于dfn[ ],如果不相等,那么不做处理,直接返回,如果相等的话就说明该节点是一个集合的代表元素,我们在将刚才栈中的所有在该节点后面入栈的还在栈内的元素(包括它本身)出栈,这些元素就是一个强联通分量。这样的正确性是显然的,我们相当于将所有已经构建的集合保存了下来,而这些集合都是由相交环构成的,而相交环构成的集合一定是一个强联通分量,,所以我们构造出来的集合就一定是原图的强联通分量!
以上就是强联通分量的讲解,,下面让我们来看看这道题:
题目的大意就是让我们判断这些点是否在一个强联通分量里,那么我们就可以以随意的一个节点为起点,向外dfs,如果在回溯过程中发现有一个节点的dfn=low,并且该节点不是我们最初dfs的起点,那么显然所有点构不成一个集合,就不符合题目要求,,如果我们最终求出的一个集合的元素个数少于点的个数,说明还有一部分点与该集合不联通,显然也不符合题目要求,要输出No。排除掉这两种结果后,输出Yes就可以了。由于本题不用统计每个强联通分量的具体元素,所以我就把栈给省了。。
下面是代码:
#include<iostream>#include<climits>#include<cstdio>#include<cstring>#include<algorithm>#define N 10010#define M 100010#define INF INT_MAXusing namespace std;int dfn[N],low[N],now=0,ans,tot,head[N];bool v[N],b[N]; // v[]标记是否在当前dfs路径上 b[]标记是否访问过 struct Edge { int to,next;Edge() { next=to=-1; } }edge[M];void Add_edge(int a,int b) { Edge &x=edge[tot];x.to=b,x.next=head[a],head[a]=tot++;}int in() { char c=0;int s=0;bool v=false; while(c<'0'||c>'9') c=getchar(),v=c=='-'; while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar(); return v?-s:s;}void input() { int a=in(),b=in(); Add_edge(a,b);}int dfs(int o) { if(v[o]) return low[o];if(b[o]) return INF; v[o]=b[o]=true;low[o]=dfn[o]=now++; for(int i=head[o];i!=-1;i=edge[i].next) { low[o]=min(low[o],dfs(edge[i].to)); } if(low[o]==dfn[o]&&o!=1) ans=-INF; else ans++; return low[o];}int main() { while(1) { int n=in(),m=in();if(n==m&&m==0) break; memset(head,-1,sizeof(head));memset(v,false,sizeof(v));memset(b,false,sizeof(b)); for(int i=1;i<=m;i++) input();dfs(1); if(ans==n) puts("Yes");else puts("No"); ans=0;tot=0;now=0; } return 0;}
- 强联通分量简讲(Tarjan算法)&&HDU 1269 迷宫城堡
- HDU 1269 迷宫城堡 (强联通分量,Tarjan算法)
- [ACM] HDU 1269 迷宫城堡(Tarjan算法求强联通分量)
- HDU 1269 迷宫城堡(tarjan强联通分量)
- hdu 1269 迷宫城堡(强连通分量 Tarjan算法)
- HDU1269 迷宫城堡 强联通分量Tarjan算法
- HDU - 1269 - 迷宫城堡 (tarjan求强连通分量)
- HDU 1269:迷宫城堡 (Tarjan强连通分量)
- HDU ACM 1269迷宫城堡->有向强连通分量(tarjan算法实践)
- HDU 1269 迷宫城堡 强联通分量模板存放处
- HDU 1269 迷宫城堡 [强连通分量] [Tarjan]
- HDU 1269 迷宫城堡 Tarjan强连通分量
- HDU 1269 迷宫城堡【Tarjan强连通分量 模板】
- 迷宫城堡 Tarjan求出强联通分量的个数
- 迷宫城堡+tarjan+强联通分量基础题
- hdu1269 迷宫城堡 tarjan求强联通分量
- HDU OJ 1269 迷宫城堡【有向图强连通分量的Tarjan算法 入门】
- hdu 1269 迷宫城堡 tarjan算法求有向图的强连通分量
- Linux yum 软件安装
- synchronized使用
- 有关Java链表概念的有趣问题
- javascript笔记总结一
- mysql表导出导入测试(utf8-utf8)
- 强联通分量简讲(Tarjan算法)&&HDU 1269 迷宫城堡
- java--previousIndex()
- hadoop学习序曲之linux基础篇--linux的安装和使用
- 不完全node实践教程-第一发
- 理解红黑树
- 【服务器编程】MYSQL数据库连接池封装
- iOS10本地推送和远程推送的原理
- 01背包
- Git 本地仓库操作